Hatena::ブログ(Diary)

でこすけの日記

2010-12-22 GHCiを電卓として使ってみる

この記事はHaskell Advent Calendarのために書いたものです。

自己紹介

dekosukeと申します。

大学の専門は理論物理で、色々あって今はWebプログラマーをやっています。仕事で使ってる言語はPHPですが、業務外ではPHPは使いません*1

Haskellマスターではないので、黒魔術(?)はあまり使えないです。最近は簡単な計算をHaskellで書いたり、Anarchy GolfというパズルをHaskellでやったりしてます。

ぼくの言語遍歴ですが、C/C++PerlPythonOCamlHaskellという感じでHaskellにたどり着きました。現在使用しているのはHaskell/Python/C++の3つです。

はじめに

Pythonチュートリアルに、形式ばらないPythonの紹介という項があります。Pythonチュートリアルは一部しか読んでいないのですが、この記事は結構印象に残っています。

内容はPythonインタラクティブシェルの紹介なのですが、それまで使っていた言語ではインタラクティブシェルがなかったので新鮮に思いました。

インタラクティブシェルとは、プログラミング言語を1行ずつ入力しながら実行結果を確認できるモードのことです。関数型言語スクリプト言語のいくつかで提供されています。行ごとの入力の結果が見られ、文法のエラーを逐次説明してくれるので、最初に新しい言語を始める際のとっかかりとして良いのではないかと思います。


で、話をHaskellに戻します。Haskellにもインタラクティブシェルがあるんですけど、Haskellだとあまりプッシュされていない気がします。

Haskellで最初に動くプログラムを書こうとすると、IOモナド周りとか色々はまりますよね。いざHaskellを始めようというのにいきなり入出力で困るのもどうかという感じなので、そこらへんを遅延評価(?)する意味でもインタラクティブシェルから入るのはどうでしょう、と提案してみます。

というわけで、Haskellデファクトスダンダード的な処理系であるGHCインタラクティブシェル(GHCi)*2で簡単な計算をしてみます。


計算編

ghciを起動しましょう

$ ghci
GHCi, version 6.12.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude>
四則演算

普通に書けます。

Prelude> 1*2+3*4
14
Prelude> 100*1.05
105.0
Prelude> 400/7
57.142857142857146

整数の除算はdivで。

Prelude> div 400 7
57

divを演算子のように中置にすることもできます。

Prelude> 400 `div` 7
57
初等関数(sinとかlogとか)

これも普通に使えます。

Prelude> sin 0.5
0.479425538604203
Prelude> log 10
2.302585092994046
Prelude> sqrt 4
2.0
Prelude> exp 10
22026.465794806718
前回の結果の利用

itで前回の計算を参照できます*3

Prelude> 1+2
3
Prelude> it*it
9

letで変数束縛できます。

Prelude> let three = 1+2 in three + 2
5
リスト

手続き型原語における配列のようなもの、リストです。

1から10までの数値のリスト

Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]

無限リスト。行ごとに評価されるので(ry

Prelude> [1..]
[1,2,3,4,5,6...(そのまま無限にお待ち下さい)

無限リストの前から5要素だけ取ってきます。ちゃんと停止します。

Prelude> take 5 [1..]
[1,2,3,4,5]

フィボナッチ数列のリスト*4。fibが無限リストで、takeで最初の10要素だけのリストを作ります。

Prelude> let fib = 1:1:zipWith (+) fib (tail fib) in take 10 fib
[1,1,2,3,5,8,13,21,34,55]
再帰処理

Haskellにはforとかforeachのようなループはないので、適切なリスト操作関数*5再帰関数を利用して繰り返し処理を行います。

リストの値を合計するには、sum関数があります。

Prelude>sum [1..10]
55

より一般的な関数foldrをsumの代わりに使うこともできます。

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

自前でmysum関数を定義することもできます。

Prelude> let { mysum [] = 0 ;  mysum (x:xs) = x + mysum xs } in mysum [1..10]
55

リストから特定の要素の場所を探す関数も書けます。下記のsearch関数はリストの何番目に要素が現れるかを返します。

Prelude> let search xs t = length $ takeWhile (/= t) xs in search [1..10] 4
3
ちょっと意味のある計算

なんだか味気ないので、少し意味のある計算もしてみましょう。

30人のクラスで、誕生日が同じペアがいる確率を求めてみます。

Prelude> 1 - ( foldr (*) 1 [ (365-n)/365 | n<-[0..29] ] ) 
0.7063162427192686

3つの辺の長さ[1,4,4]が与えられたとき、それが三角形を作れるかどうか*6をTrue/False判定します。

Prelude> let ar = reverse$Data.List.sort [1,4,4] in head ar < (sum$tail ar)
True

お菓子のおまけがランダムで10種類あるとき、おまけを全種類そろえるために必要なお菓子の個数の期待値を調べます。*7

Prelude> sum [ 10/n | n<-[1..10] ]
29.289682539682538
数値のプロット

さっきのお菓子のおまけを集めるために必要なお菓子の個数を、1-10個に変えてみた場合の期待値の変化をプロットします。

Prelude> :m Graphics.Gnuplot.Simple
Prelude Graphics.Gnuplot.Simple> plotList [] (map (\n->sum [ n/x | x<-[1..n] ]) [1..10])

おわりに

以上、Haskellインタラクティブシェルで簡単に出来る計算を書いてみました。簡単な数値計算だと、Haskellはパズルみたいで面白いですね。

Haskellってよく分からないとかとっつきづらいとか思っている人は、とりあえず

$ ghci

とか打ってみるといいと思います!

*1:理由はお察し下さい

*2:ここではHaskell処理系としてGHC(The Glasgow Haskell Compiler)を使うことを仮定しています。とくに理由がない限りGHCを使うのがよいと思います

*3:itの正確な意味についてはGHCi-プロンプトで対話的に評価するを参照

*4:最初に見るとびっくりするHaskellフィボナッチ数列

*5:あるいはモナド操作関数

*6:一番大きい辺<残りの2辺の和

*7Coupon Collector問題ですね