TIM Labs

2012年11月アーカイブ

求めたい数といえば、やはり一番は円周率、πであろう。
円周率を正則連分数で示す方法もあるのだが、美しくない。
せっかく連分数を使っているし、円周率はもっと限りなく美しく表現できなければウソだ。
ということで、多くの数学者が研究しており、正則連分数ではないが、限りなく美しいものがある。
その1つがこれだ。
詳しくはWikipediaの連分数を見てほしい。
ここでは、この連分数をHaskellで計算することだけに集中する。

renbunsuu-pi-1.png

Haskellで実現するとき、延々と展開されていくところを、どう関数で表現するかだ。
最初を除けば、分数の頭にある6は固定値なので、そのまま書いてしまおう。
そして、今度は分数部分の分子が奇数で、どんどん増えていく。
この部分をどう実現しようか。

[1,3..] というリストを与えることで、実現できないだろうか。

√2を思い出そう。

let ratSqrt2 n = foldr (\x y -> x+1/y) (1%1) (1 : (take n [2,2..]))

ここで、ラムダ関数を、

(\x y -> 6+x*x/y)

と書くだけで良いのではないだろうか。とりあえず、これで実験してみよう。

Prelude Data.List Data.Ratio> let pi' n = foldr (\x y -> 6+x*x/y) (1%1) (take n [1,3..])
Prelude Data.List Data.Ratio> pi' 10
16172869847 % 2633465835
Prelude Data.List Data.Ratio> fromRational (pi' 10)
6.141287132741557

ちゃんと動いているようにも見えるが、整数部分が6になってしまっている。

ということで、リスト[1]が与えられた場合を考えてみよう。

すると、(\x y -> 6+x*x/y) に対して、 x=1, y=1 のときの値が結果として出てくる。
それは、 6+1*1/1 = 7 となる。

円周率の連分数展開では、最初だけなぜか3になっているので、3を引かないといけないので、全体から3を引くことにしよう。

Prelude Data.List Data.Ratio> let pi' n = (foldr (\x y -> 6+x*x/y) (1%1) (take n [1,3..]))-3
Prelude Data.List Data.Ratio> pi' 10
8272472342 % 2633465835
Prelude Data.List Data.Ratio> fromRational (pi' 10)
3.141287132741557
Prelude Data.List Data.Ratio> fromRational (pi' 20)
3.1415581036454996
Prelude Data.List Data.Ratio> fromRational (pi' 30)
3.1415827539476537
Prelude Data.List Data.Ratio> fromRational (pi' 50)
3.141590571791174
Prelude Data.List Data.Ratio> fromRational (pi' 100)
3.141592398533554
Prelude Data.List Data.Ratio> fromRational (pi' 500)
3.1415926515817754
Prelude Data.List Data.Ratio> fromRational (pi' 2000)
3.141592653558512
Prelude Data.List Data.Ratio> fromRational (pi' 10000)
3.141592653589543

1万までやっても、どうも精度が怪しい。
Haskellでは、 pi とやるだけで実は円周率は使えるのだ。

Prelude Data.List Data.Ratio> pi
3.141592653589793

この連分数展開は死ぬほど収束が遅いようだ。
はっきり言って、使い物にならない。
別の連分数展開に挑戦しよう。

RenbunsuunoFushigi.jpg黄金比のときは、連分数の計算に、[1,1..]のリストを使ったのだが、実はこのリストを適当に変えるだけでいろいろな数が求められるのだ。

連分数の分子部分が常に1になっているものを正則連分数という。
このとき、分母になる数だけを並べ、さらに最初の分数の前に加えられる数を;の前に置いた表現がある。

黄金比の場合には、 [1;1,1,1,..] となって全部1になってしまう。

この表記でいくと、√2(2の平方根)は、[1;2,2,2,...] となる。
詳しいことは、Wikipediaの√2の説明、あるいは連分数を見て確認して欲しい。

ということで、√2の連分数のn次近似は、リスト [1,2,2,...,2] (2がn個) を用意すれば計算できる。

*Main Data.List Data.Ratio> let ratSqrt2 n = foldr (\x y -> x+1/y) (1%1) (1 : (take n [2,2..]))

*Main Data.List Data.Ratio> ratSqrt2 10
11482 % 8119
*Main Data.List Data.Ratio> ratSqrt2 20
77227930 % 54608393

これで分数としては良いはずだが、分かりにくいので浮動小数点表示させてみよう。

*Main Data.List Data.Ratio> fromRational (ratSqrt2 10)
1.4142135731001355
*Main Data.List Data.Ratio> fromRational (ratSqrt2 20)
1.4142135623730954
*Main Data.List Data.Ratio> fromRational (ratSqrt2 30)
1.4142135623730951
*Main Data.List Data.Ratio> sqrt 2
1.4142135623730951

20次でほぼ有効範囲になっているようだ。収束の速度はまあまあか。

しかし、計算してみたい数といったら√2ではなくて、あれだろう。

フィボナッチ数列という有名な数列がある。 1 1 2 3 5 8 ... と続くアレだ。漸化式でいうと、

\begin{eqnarray*} F_1 &=& 1 \\ F_2 &=& 1 \\ F_{n+2} &=& F_n + F_{n+1} \ \ (1 \leq n) \end{eqnarray*}

っていうこいつである。

で、こいつの一般項というのが知られている。それは以下の通りである。...らしい。

$$ F_n = \frac{1}{\sqrt{5}}\biggl\{\biggl(\frac{1 + \sqrt{5}}{2}\biggr)^n - \biggl(\frac{1 - \sqrt{5}}{2}\biggr)^n\biggr\} $$

え、えー? いやちょっと待ってよだってほら整数の数列だし一般項に無理数入ってるとか大分意味わかんないんですけどこれ大丈夫なの本当に?

うーん。確認してみよう。

fibDouble :: Integer -> Double
fibDouble n = ( ((1 + sqrt 5)/2)^n - ((1 - sqrt 5)/2)^n ) / sqrt 5

main :: IO ()
main = mapM_ (print . fibDouble) [1..10]

runghc すると:

1.0
1.0
2.0
3.0
5.0
8.0
13.0
21.0
34.0
54.99999999999999

うん。あって...る。微妙に誤差があるのだがまあ四捨五入すれば十分使えるレベルのハズ。誤差が怪しかったひとつ前の 9 番目からを改めてやってみよう。

fibDouble :: Integer -> Integer
fibDouble n = round $ ( ((1 + sqrt 5)/2)^n - ((1 - sqrt 5)/2)^n ) / sqrt 5

main :: IO ()
main = mapM_ (print . fibDouble) [9..15]

えいや

34
55
89
144
233
377
610

良いのでは?

一般項を使っているので、 n 番目一個だけを知りたい、という要求においてはこの実装は確かにそれなりに速い。ただ内部の計算が浮動小数なので、100 番目くらいを求めようとするともうもはや求まらない。

main = print $ fibDouble 100

実行すると

354224848179261800448

あってる? いいえ、残念。100 番目の本当のフィボナッチ数は 354224848179261915075 なのだ。(※ちなみにこの数を Google さんで検索すると、本記事執筆時点で 7,000 件以上ひっかかってそれなりに有名な数であることがわかる。)

もう浮動小数なんかダメだ。信用ならない。まったく、たかが 100 番目くらいで正しい答えを得られないようでは"なってない"といわざるを得ないではないか。やっぱりここは誤差なしで扱えるイケメン、じゃなかった、有理数に限る。

でも、どうやって? 元の式に無理数が含まれている以上、有理数だけで計算するなんてできっこないじゃないか。

『関数プログラミング』4.5 畳み込み(fold)関数 手習い中RenbunsuunoFushigi.jpg

ここで、有理数(分数)の計算を色々試してみよう。

*Main Data.Ratio> 1/1+1/2+1/3+1/4+1/5
2.2833333333333332

ここで、/ のうち、1つだけを % にすると、どうなるだろうか。

*Main Data.Ratio> 1/1+1/2+1%3+1/4+1/5
137 % 60

ということで、どうやら % が一つでも入っていると、全体が有理数として計算されるようだ。

前回の黄金比の計算で出てきた無名関数 (\x y -> x+1/y) の除算のところを % に変えてみよう。

*Main Data.Ratio> foldr (\x y -> x+1%y) 1 [1,1,1]

    Occurs check: cannot construct the infinite type: a0 = Ratio a0
    In the second argument of `(%)', namely `y'
    In the second argument of `(+)', namely `(1 % y)'
    In the expression: x + (1 % y)

というエラーメッセージが出てきてしまった。
the infinite type ということで、型にうるさいHaskellの型決めの怒りを買ってしまった。

ということで、無名関数は (\x y -> x+1/y) のままにして何とかならないか考えよう。
最初の実験から、どこかに有理数が含まれて入れば、全体を有理数にしてくれるらしい。
だから、foldrの計算で使われる最初の数を有理数にしてしまえば、全体が有理数になるのではないだろうか。
Haskells.jpgのサムネール画像

最初の数は、無名関数の次の引数の1である。
1のままではダメだから、有理数に型変換する関数 toRational を使ってみよう。

*Main Data.Ratio> foldr (\ x y -> x + 1 / y) (toRational 1) [1, 1, 1]
5 % 3

ちゃんと計算できた。(確認は読者に任せた!)

しかし、toRational 1 は長いので、1%1で置き換えてしまおう。

*Main Data.Ratio> foldr (\ x y -> x + 1 / y) (1%1) [1, 1, 1]
5 % 3

[1,1,1] とかで必要個数並べるのは大変なので、takeを使って個数を指定しよう。

*Main Data.Ratio> foldr (\ x y -> x + 1 / y) (1%1) (take 3 [1,1..])
5 % 3
*Main Data.Ratio> foldr (\ x y -> x + 1 / y) (1%1) (take 10 [1,1..])
144 % 89
*Main Data.Ratio> foldr (\ x y -> x + 1 / y) (1%1) (take 20 [1,1..])
17711 % 10946

しかし、俗人には分数のままではよく分からないので、浮動小数点に直してみよう。

*Main Data.Ratio> fromRational (foldr (\ x y -> x + 1 / y) (1%1) (take 10 [1,1..]))
1.6179775280898876
*Main Data.Ratio> fromRational (foldr (\ x y -> x + 1 / y) (1%1) (take 20 [1,1..]))
1.618033985017358
*Main Data.Ratio> fromRational (foldr (\ x y -> x + 1 / y) (1%1) (take 30 [1,1..]))
1.6180339887496482
*Main Data.Ratio> fromRational (foldr (\ x y -> x + 1 / y) (1%1) (take 40 [1,1..]))
1.618033988749895
*Main Data.Ratio> fromRational (foldr (\ x y -> x + 1 / y) (1%1) (take 50 [1,1..]))
1.618033988749895

40項まで取れば、この有効桁数までの精度が得られている気がする。
直接黄金比を計算してみよう。

*Main Data.Ratio> (1+(sqrt 5))/2
1.618033988749895

ということで、黄金比を連分数を使って計算することができた。
しかし、数を正確に計算するとなると、やはりアレを計算しなくては(つづく)


※なお、ここでの計算は、数学の本に書いてある途中までの近似とは違う値になっている。
それは、再帰のラムダ式が楽になるようにしているためであるが、近似が進めば無視できるようになる。
詳しいことは省略。

RenbunsuunoFushigi.jpgHaskellで、foldrについて考えていたら、なぜか BLUE BACKS の『連分数の不思議』が机の上にあった。
表紙(帯)には、黄金比を連分数で示していた。

黄金比Φ= 1+1/(1+1/(1+1/(1+1/(1+1/(1+.....

実際には、これをちゃんと分数の形に書くと、とても美しい連分数になる。

この連分数を見ていると、右の方から畳み込んでいることが分かる。
つまり、これって、foldr そのものではないだろうか。

ここで、連分数を、もうすこし一般的な、正則連分数について考えることにしよう。

正則連分数 = a0+1/(a1+1/(a2+1/(a3+1/(a4+1/(a5+.....

という連分数だ。分数のまま書くのは大変なので、[a0;a1,a2,a3,a4,...]という表記方法がある。

黄金比Φ の場合、 [1;1,1,1,...]  となる。

さて、[a0;a1,a2,a3,a4,...]をリストとして作り、これからfoldrを使って連分数を計算してみよう。

連分数の分数1回分の演算を★で表すと、

x ★ y ->  x + 1 / y

と書けるのだ。詳しくは、自分で確認しよう。

これを1つの補助関数として定義しても良いのだが、この程度は1つの関数の中に書き込んでしまいたい。
そんなことをするために、名前をつけなくても使える無名(ラムダ)関数というものがRealWorldHaskell.jpgある。
このあたりの知識は、オライリーの "Real World Haskell" から得たものだ。
こちらの本も、おなじ訳者だ。

名前がない関数なので、名前を書かなくても良い場所に書かねばならない。
foldr の最初の引数は関数なので、ここに書いてしまえば良いはずだ。

foldr (\x y -> x+1/y)

さて、この演算を畳み込むときの最初の値 e は何が妥当であろうか。
0にしてしまうと、最初に無名関数のyのところに使われるのでまずい。
ということで、1にしてみよう。

foldr (\x y -> x+1/y) 1

あとは、1が延々と続くリストを書けば完成だ。

*Main Data.Ratio> foldr (\x y -> x+1/y) 1 [1,1..]
  C-c C-cInterrupted.
*Main Data.Ratio>

しかし、いつまで経っても帰ってこないので殺した。

[1,1...] という無限リストを与えたのがまずかったようだ。
リストの長さを短くして様子を見よう。

*Main Data.Ratio> foldr (\x y -> x+1/y) 1 [1,1]
1.5
*Main Data.Ratio> foldr (\x y -> x+1/y) 1 [1,1,1]
1.6666666666666665
*Main Data.Ratio>

連分数なのに、分数ではなく浮動小数点という誤差を伴う計算に成り下がってしまった。
有理数(Rational)で計算しなくてはダメだ。
どうすれば出来るだろうか?

『関数プログラミング』3.5 有理数
Haskells.jpgのサムネール画像
foldr について、格好良い使い方を考える前に、ちょっとだけ前に戻ろう。

まず、1 / 2 + 1 / 3 という分数計算をやってみよう。

Prelude Data.Ratio> 1 / 2 + 1 / 3
0.8333333333333333

浮動小数点数になる。実際に型を確認すると

Prelude Data.Ratio> :t 1 / 2 + 1 / 3
1 / 2 + 1 / 3 :: Fractional a => a

ところで、3.5 例:有理数 には、有理数 Ratioanl の定義などが載っている。

しかし、最新のGHCiなら、何も自分で用意しなくたって、インポートするだけで使える。
ネット上には、自分で有理数を用意する方法がいっぱい転がっているが、清く正しく、GHCiに用意されているのを使ってみよう。
より横着な方法を考えるのが、プログラマのあるべき姿だ。

ということで、用意されているものを利用できるようにしよう。

Prelude> import Data.Ratio
Prelude Data.Ratio>

import の代わりに、 :m Data.Ratio でもよい。

すると、分数同士の演算が分数のまま可能になる。

さて、 / の代わりに % を使って同じことをしてみよう。

Prelude Data.Ratio> 1 % 2 + 1 % 3
5 % 6
Prelude Data.Ratio> :t 1 % 2 + 1 % 3
1 % 2 + 1 % 3 :: Integral a => Ratio a
Prelude Data.Ratio>

% で、割り算結果が分数のままになる。

ということで、分数のまま、ちょっと級数展開の計算をしてみよう。

簡単なところで、

*Main Data.Ratio> exp 1
2.718281828459045

を、級数展開の和を実際に計算して求めることにしよう。

e = Σ1/n!  (n=0..) で求まることになっているので、
最初のn項までの和を求めてみよう。
この級数は、結構収束が良いはずだ。

ずは、階乗の計算だが、今回は foldr を使って作ってみた。
foldr を使えば、再帰の必要も無いのだ。

fact :: Integer -> Integer
fact n = foldr (*) 1 [1..n]

*Main Data.Ratio> fact 10
3628800
*Main Data.Ratio> fact 20
2432902008176640000

となって、動いているようだ。
後は、だらだらと級数展開の通りに書いてみた。
右のmap で、階乗を求め、次のmap で逆数にし、foldr で全部加えた。

import Data.Ratio

expseq    :: Integer -> Rational
expseq  n =  foldr (+) 0 (map (1 %) (map (fact) [0..n]))

fact :: Integer -> Integer
fact n = foldr (*) 1 [1..n]

さて、実行してみよう。

*Main Data.Ratio> expseq 3
8 % 3
*Main Data.Ratio> expseq 4
65 % 24

分数だとよく分からないが、差をとると、1つの項が出るはずだ。

*Main Data.Ratio> (expseq 4) - (expseq 3)
1 % 24
*Main Data.Ratio> (expseq 5) - (expseq 4)
1 % 120

これで、動作確認もできた訳だが、俗人は浮動小数点が好きらしいので、直してみよう。

*Main Data.Ratio> fromRational (expseq 4)
2.7083333333333335
*Main Data.Ratio> fromRational (expseq 5)
2.716666666666667
*Main Data.Ratio> fromRational (expseq 6)
2.7180555555555554
*Main Data.Ratio> fromRational (expseq 10)
2.7182818011463845
*Main Data.Ratio> fromRational (expseq 15)
2.7182818284589945
*Main Data.Ratio> fromRational (expseq 20)
2.718281828459045
*Main Data.Ratio>

ということで、e が計算できた。
実際の計算では、精度のことも考えて、どこで打ち切るかを考えねばならぬだろうが、省略する。

有理数として計算をしているので、foldl, foldr のどちらを使っても結果に誤差は出ない。
まだ、foldrの持ち味がほとんど出ていない。
ちゃんと foldr が生きてくる例を考えてみよう。
『関数プログラミング』4.5 畳み込み(fold)関数

Haskellでとても重要なのが畳み込み関数であろう。Haskells.jpgのサムネール画像

リスト [x1,x2,x3,x4] が与えられたとき、

x1 ★ (x2 ★ (x3 ★ (x4 ★ e)))

の計算をしたくなることがある。★は何らかの2項演算子。

このように、右から順番にくくっていく演算をリストに対して行うのが右畳み込み関数 foldr である。
この定義は以下のようにされている。

foldr            :: (a -> b -> b ) -> b -> [a] -> b
foldr f e []     = e
foldr f e (x:xs) = f x (foldr f e xs)

そして、以上のことを右側からではなく、左側から処理していくのが foldl だ。

foldl            :: (b -> a -> b ) -> b -> [a] -> b
foldl f e []     = e
foldl f e (x:xs) = foldl (f e x) xs

しかし、これでは、わかり辛いだろう。
だから、実例がネッと上に結構転がっている。

foldlとfoldrの比較をするのに、(+) を使っているのをよく見る。

たとえば、リスト [1..10] の和を求めるのに、

Prelude> foldl (+) 0 [1..10]
55
Prelude> foldr (+) 0 [1..10]
55

として、結合法則が成り立つ演算の場合に、foldr と foldl の結果が同じだと。

結合法則が成り立たない例として、 (-) を使ったのをよく見かける。

Prelude> foldl (-) 0 [1..10]
-55
Prelude> foldr (-) 0 [1..10]
-5

これは、

(..(0)-1)-2)-3)-4)-5)-6)-7)-8)-9)-10
0-(1-(2-(3-(4-(5-(6-(7-(8-(9-(10)..)

の差に他ならない。つまらない、無理矢理な例に過ぎない。

こんな説明をされても嬉しくない。
これでは、foldr や foldl の屁理屈を述べたに過ぎない。
限りなく無意味な例ではないだろうか。

もっと有用な使い道がきっとあるはずだ。
考えてみよう。


Haskells.jpgのサムネール画像
『関数プログラミング入門』(IFPH)手習い中

ピタゴラス数の高速な求め方について、あれこれ考えた。
ピタゴラス数とは、x*x+y*y==z*z を満たす整数の3つ組 x,y,z のすべての組み合わせを作ってからやると、すごい量になる。
せめて、xとyとかだけにすれば良いか?

などということは全然考えずに、整数 m, n (m>n>0) に対して、
(m*m-n*n, 2*m*n, m*m+n*n) の3つ組がピタゴラス数になるというのを使った。

もちろん、互いに素を除かないとダメだが、それはプログラム中でちょこっと対応。

これですべてのピタゴラス数を見つけることができる証明は省略!
ネットで調べるなり、整数論の本を見るなりして探しましょう。

ということで、新しいプログラム。

triads         :: Int -> [(Int,Int,Int)]
triads n       =  [(x,y,z) | (x,y,z) <- map toabc (doubles (findlimit n)), z<=n, gcd x y == 1]

findlimit    :: Int -> Int
findlimit n  = fromIntegral (truncate (sqrt (fromIntegral n)))

doubles      :: Int -> [(Int,Int)]
doubles n    =  [(x,y) | x <- [1..n-1], y <- [x+1..n]]

toabc        :: (Int,Int) -> (Int,Int,Int)
toabc (n,m)  = (m*m-n*n,2*m*n,m*m+n*n)

これを実行すると、どうなるだろうか。

*Main> triads 20
[(3,4,5),(15,8,17),(5,12,13)]

ということで、20以下のピタゴラス数が全部求まっている。
出てくる順番は、m,n に依存した順番なので、分かり辛いかも。
ピタゴラス数としての順番にしたい人は、自分で変更してください。

さて、どこまでできるだろうか。
全部表示されても困るので、ピタゴラス数の個数だけを求めてみよう。

*Main> (length.triads) 100
16
*Main> (length.triads) 1000
158
*Main> (length.triads) 10000
1593
*Main> (length.triads) 100000
15919
*Main> (length.triads) 1000000
159139
*Main>

こんな感じで、非力な32ビットノートパソコンでも、簡単に求まるのだ。

1000000(100万)までの最後のピタゴラス数は

*Main> (last.triads) 1000000
(1413,998284,998285)

となる。y+1==z の場合になっている。まあ、これは偶然だが。

ということで、Haskellは非常に高速!
というより、計算の仕方が良かっただけかな。

数学的にうまいやり方が見つかっている場合には、使いましょう。
知らないと馬鹿なログラムを書いてCPUを無駄遣いします。
1万倍、あるいはそれ以上の差が出ることはよくあります。

問題

Vim は何でもできます。 何でもできるのですから当然 Vim で素数を列挙することもできます。 ただ列挙するだけだと面白みに欠けますから、 Vim で最少の手数で素数を少ない順に100個だけ列挙するきっとモテるに違いありません。 一体どうすればよいのでしょうか。

Haskells.jpgのサムネール画像

『関数プログラミング入門』(IFPH)手習い中

ユニコードで遊んだ結果を書き続けようかと思っていた。 しかし、文字化けがとても発生しやすいので、本に戻ろう。

第4章リストの中に、あの有名なピタゴラス数を求めるというのがある。

xx+yy==z*z を満たす整数の3つ組(x,y,z)を求めよ

とりあえず、本の通りにやってみた。

『関数プログラミング入門』第3章 数値 なんだが、ごちゃごちゃ面倒なことが書かれていて、 読んでいると眠くなってきたので、適当に読み飛ばして、第4章リストで少し遊んでみよう。

プログラミングの本の読書とは、本はまあ普通に読んだり、読み飛ばしたりしながら、 本の趣旨とは違うことをやってみることだ。

haskellでは、無限リストが扱えるので、そのあたりが楽しいのだが、 無限リストは第9章になっている。

でも、この本、こんなものが載っているのだ。

from :: Integral a => a -> [a]
from n = n : from (n+1)

リストで再帰つかって、無限リストを作っているのだ。 で、試してみた。

Haskells.jpg

『関数プログラミング入門』3.3 畳み込み関数 の続きをやろう。 数の定義から出発して、 , ×, の計算を定義してきた。

ここで、再帰定義のお勉強の王道である、階乗とフィボナッチ数を求めてみよう。 とりあえず、何も考えず、本を写経してみた。

data Nat = Zero | Succ Nat
     deriving (Eq,Ord,Show)

foldn   :: (a -> a) -> a -> Nat -> a
foldn h c Zero     = c
foldn h c (Succ n) = h (foldn h c n)

m + n = foldn Succ m n
m × n = foldn (+ m) Zero n
m ↑ n = foldn (× m) (Succ Zero) n

fact :: Nat -> Nat
fact = snd . foldn f (Zero, Succ Zero)
     where f (m,n) = (Succ m, Succ m × n )

fib :: Nat -> Nat
fib = fst . foldn g (Zero, Succ Zero)
    where g (m,n) = (n,m + n)

ちょっと実行して、動作確認してみよう。

Haskells.jpg

『関数プログラミング入門』 2.2 文字 のところをで文字処理が若干書かれているが、漢字(Unicode)に限ってちょっといじってみた。

新しいGHCiでは、漢字に対応している、というかUnicodeに対応しているようなので、もうちょっと確認してみた。

漢字を文字および文字列として表示してみると、次のようになる。

Prelude> show '一'
"'\\19968'"
Prelude> show "漢字"
"\"\\28450\\23383\""
Prelude>

ユニコードの10進数表示で出てきてしまった。 文字として、そのまま表示すのに putChar, putStrLn を使ってみた。 Ln をつけると、予想通り改行される。

Prelude> putChar '一'
一Prelude> putStrLn "漢字"
漢字

では、 '一' を数字に直してみようということで、ordを使ってみた。

Haskells.jpg

第3章は数値である。 ちゃんと、自然数を扱ってみようというわけだ。 それで、次のように加算(+)を本に従って定義してみた。

data Nat = Zero | Succ Nat
     deriving (Eq,Ord,Show)

(+)  :: Nat -> Nat -> Nat
m + Zero    = m
m + Succ n  = Succ ( m + n )

しかし、これではエラーになってしまった。

関数プログラミングの世界の標準教科書である Introduction to Functional Programming using Haskell の翻訳が、つい先日出た。

Haskells.jpg

オーム社からで、翻訳は Haskellで有名な山下さんである。
定価3990円(税込)はちょっと高く感じるかもしれないが、これは原書よりもはるかに安く設定されており、オーム社の意気込みが感じられる。

A5判で、約400ページの本である。

既存の、つまり手続き型のプログラミング言語の本ばかり読んできた人は、かなり面食らうのではないかと思われる本である。
ちょっと見たところ、プログラミングの本というより、関数プログラミングとは何か書いた本といった雰囲気だ。
もちろん、例題などでは Hasekellが使われているのだが、解答がいちいち載っているわけではない。ネットで答、解説を探すと、著名な本名なのでかなり見つかる。もちろん、英語なのだが。
ということで、本書を読もうとして、Haskellをインストール(正しくはアップデート)した。
なかなか解り難いところもあって、じっくりと、そしてしばしばページを逆戻りして再確認などしないといけない本である。
圏論との関係も、本書のあちこちで、ちょろちょろと説明があるようだ。

読み進めながら、なにかちょろちょろと書き進められたらと思っている。


このアーカイブについて

このページには、2012年11月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2012年10月です。

次のアーカイブは2012年12月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。