cprb -- Ruby風Cプリプロセッサ

C++や、それ風の言語で書いていると徒労感に襲われる時ってありませんか。

マクロはほとんど使用禁止みたいなもんだし、そもそも文字列を繋ぐ程度の能力しかありません。テンプレートはコンテナとか作ってるぶんにはいいのですが、少々凝ったことをしだすと関数オブジェクトだのtypenameだのが乱舞しだしてタイプ量が減ったのか増えたのか分からないことになり、チューリング完全とか言い出すと闇が待ち受けています。マクロを使って不安に苛まれるか、テンプレートを使って何とかするか、諦めて手で繰り返し書くかの不愉快な三択。

こいつはそもそもC++メタプログラミング能力が貧弱なのがいかんのです。


というわけで、Ruby風にCをプリプロセスできるというものを作ってみました。設計方針は

  • 実装が簡単、見た目がシンプル
  • emacsc++-modeで使うことが前提

です。

サンプルその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()つけるのが面倒なんですけれども。

長崎も始まっています!!

看板をかけたらしい
http://www.nagasaki-u.ac.jp/info/p_release/2010/20100507a.pdf

ニュースになっている
http://www.nagasaki-np.co.jp/kiji/20100511/09.shtml

そしてマキーノ先生がフォローしている
http://www.artcompsci.org/~makino/journal/journal-2010-05.html#14