cprb -- Ruby風Cプリプロセッサ
C++や、それ風の言語で書いていると徒労感に襲われる時ってありませんか。
マクロはほとんど使用禁止みたいなもんだし、そもそも文字列を繋ぐ程度の能力しかありません。テンプレートはコンテナとか作ってるぶんにはいいのですが、少々凝ったことをしだすと関数オブジェクトだのtypenameだのが乱舞しだしてタイプ量が減ったのか増えたのか分からないことになり、チューリング完全とか言い出すと闇が待ち受けています。マクロを使って不安に苛まれるか、テンプレートを使って何とかするか、諦めて手で繰り返し書くかの不愉快な三択。
こいつはそもそもC++のメタプログラミング能力が貧弱なのがいかんのです。
というわけで、Ruby風にCをプリプロセスできるというものを作ってみました。設計方針は
です。
サンプルその1
いきなりですが、サンプルです。 hello-world.cpprb にこう書いておいて
#include <iostream> using namespace std; int main () { cout << "Hello World, I was born at #{Time.now} !\n"; }
cprb hello-world.cpprb --keep とやると、以下のような中間スクリプト hello-world.rb を生成し、
puts <<END0 #include <iostream> using namespace std; int main () { cout << "Hello World, I was born at #{Time.now} !\\n"; } END0
これを実行することで、以下の hello-world.cppを作ってくれます。
#include <iostream> using namespace std; int main () { cout << "Hello World, I was born at Wed Dec 08 00:00:41 +0900 2010 !\n"; }
サンプルその2
このtest-1.cpprbが
#include <iostream> using namespace std; const int N = 10; // the size of mesh /*" def xyzi() ctr = 0 ['X', 'Y', 'Z'].each{|dir| yield(dir,ctr); ctr+=1} end xyzi{|dir,| puts "*/ struct #{dir}{}; /*" } xyzi{|dir,i| puts "*/ int operator+(int x, #{dir} d) { return x + 1#{ ' *N'*i }; } /*" } puts "*/ int main () { /* calculate address at (2,4,0) */ cout << 0#{' +X()'*2}#{' +Y()'*4} << endl; return 0; }
こんな感じに変換されます。
#include <iostream> using namespace std; const int N = 10; // the size of mesh struct X{}; struct Y{}; struct Z{}; int operator+(int x, X d) { return x + 1; } int operator+(int x, Y d) { return x + 1 *N; } int operator+(int x, Z d) { return x + 1 *N *N; } int main () { /* calculate address at (2,4,0) */ cout << 0 +X() +X() +Y() +Y() +Y() +Y() << endl; return 0; }
まとめ
githubで公開しています。 https://github.com/nushio3/cprb
ちょっとサーベイして無かったので作ったのですが、いかにもありそうな発想なので、既出だよ、もっとマシなのがあるよという場合教えてください。そっちを使いに行くんで。感想などもお待ちしてます。
Haskell 超巨大実数ライブラリ
やけくそにでかい実数を適当な精度で扱えるライブラリを作ってみた。
きっかけは小数小野小町算のツイート。うん、屁理屈なんだ、すまない。
でもどんくらいでっかい数がでてくるものか、興味があったんだ。
バグとかあったら教えてください。
追記:案の定バグってたんで、テストつけてバグとって更新しました。
module Main ( DoublePlus(..), doublePlus, logB, main ) where import Control.Monad import Data.List import System.Random base = 10 data DoublePlus = Imm Double | Exp DoublePlus deriving (Eq, Ord, Show, Read) logB a = log a / log base doublePlus a = if a < 10 then Imm a else Exp $ doublePlus $ logB a eval (Imm a) = a eval (Exp a) = base ** eval a add small large = let ans = eval small + eval large in if ans < 1e10 then doublePlus ans else add' small large add' small large@(Exp b) = let increase = case small/large of Imm a -> log (1+a) in Exp (b + Imm increase) sub large@(Imm a) small@(Imm b) = Imm (a-b) sub large@(Exp a) small = let increase = case small/large of Imm b -> log (1-b) in Exp (a + Imm increase) instance Num DoublePlus where Imm a + Imm b = doublePlus (a+b) a + b = if a < b then add a b else add b a Imm a * Imm b = doublePlus (a*b) Imm a * Exp b = Exp (Imm (logB a) + b) Exp a * Imm b = Imm b * Exp a instance Fractional DoublePlus where {- assert a < b -} Imm a / Imm b = Imm (a/b) Imm a / Exp b = Imm $ a / eval (Exp b) Exp a / Exp b = Imm $ 1 / base**eval (sub b a) fromRational x = doublePlus (fromRational x) instance Floating DoublePlus where Imm a ** Imm b = doublePlus (a**b) Imm a ** Exp b = Exp (Imm (logB a) * Exp b) Exp a ** b = Exp (a * b) main = main2 main2 = do putStrLn "hi" print $ ans where ans = foldr1 (**) $ map Imm [ 1.1 , 1.2 , 1.3 , 1.4 , 1.5 , 1.6 , 1.7 , 1.8 , 1.9 , 2.0] ans2 = foldr1 (**) $ map Imm [ 1.4, 1.5, 1.6 , 1.7, 1.8, 1.9, 2.0, 1.3/(1.2-1.1)] maintest = do testSets <- fmap concat $ replicateM 1000 makeTestSet mapM_ printTest $ sort $ map badness $ testSets makeTestSet = do n' <- (randomIO) let n = 3 + mod (div n' 10000) 10 rands <- replicateM n (randomIO) let xs = map ((1.0::Double)+) rands let infinity = 2**100000 let ans = foldr1 (**) xs return $ if ans < infinity then [(ans,xs)] else [] badness (ans, xs) = let numAns = eval $ foldr1 (**) $ map Imm xs gosa = abs(numAns-ans) / ans msg = " or " ++ show ans ++ " vs " ++ show numAns ++ " for " ++ show xs in (gosa,msg) printTest (gosa,msg) = do putStrLn $ "Error was " ++ show gosa ++ " " ++ msg
はやぶさ記念写真
はやぶさの地球帰還の興奮さめやらぬ昨今ですがいかがお過ごしでしょうか。6月13日はオンラインで各種生中継が行われ翌日の朝刊はどこも1面記事ではやぶさの帰還を報じていました。生中継は大興奮でしたし、テレビや新聞の高解像度で転送された動画・写真はそれは美しいものでした。
で、その写真がどうしても欲しいなあと思ったので調べたところ、どうも新聞社は紙面に掲載した写真などを販売もしてくれるということを知りました。
修学旅行のお土産に使われる額とかとくらべると全然高くないと思うので、人類がイトカワに到達して帰ってきた記念にお一ついかがでしょうか。ご参考までに僕はこっから買えました。
http://photoarchives.asahi.com/personal.html
注文したのはこの写真です。
http://www.asahi.com/science/gallery/100614_hayabusa/111_starryhayabusa.html
世は資本主義ですのでこういうのを買っていくと、より科学報道が重視されるようになるのかなとも思います。ちなみに実家の新聞はコボちゃんですので回し者ではないです。
Haskellで開発しているオープンソースコミュニティー
ってあります?
Haskellでテスト駆動開発とかどうやるんだろ。C++でいうgoogletestみたいなのってあるのかな。思いついて、darcsのレポジトリを覗いてみたが・・・
http://allmydata.org/trac/darcs-2/browser/tests
シェルスクリプトがいっぱいある。テスト自動化されているわけではない?
http://hackage.haskell.org/package/test-framework-0.3.1
こういうのはあるな。これを使って開発してるとこあるのかな?
Haskellで型クラスを同一のリストに突っ込む
Haskellで、同じ型クラスだけど違う型のオブジェクトを、一つのリストとかにツッコんでその型クラスの関数に関してはmapとかできるようにしたいというのは、よくある願いだと思う。ていうかMonadiusつくった時にはそうする方法を知らんかったので仕方なく型を1種類だけにして全部突っ込んだのでえらいことになっていた。若気の至り
こないだ id:tanakh さんからアドバイスをもらってたのを試してみたら簡単にできた。
{-# OPTIONS -XExistentialQuantification #-} data A = forall a. Show a => A a instance Show A where show (A a) = show a as = [A 2, A 2.0, A "hoge"] main = do print as
narioでも解決済みだった。 http://d.hatena.ne.jp/mokehehe/20081002/gameobj
c++のstd::ifstreamとかってなんでstringを引数にとってコンストラクトできないの?
c++の標準ライブラリって、結構考えて作ってあるから、もし理由があるなら知りたいんですが。
ファイル名などの文字列を扱うには、char*よりstringの方が便利ですから、
ifstreamにファイル名指定する時にも、stringが直接渡せればいいと思うのですけれども。
c_str()つけるのが面倒なんですけれども。