ピタゴラス数

プログラミングHaskellの第5章演習問題より。印象的だったので…

問題
大きさnまでの正の整数を要素とするピタゴラス数を列挙する関数phを書こう

すこし親切な問題の説明
1.まず、大きさnまでの正の整数3個の組み合わせを生成してみよう
2.その組み合わせのうち、ピタゴラス数x*x+y*y==z*zをみたすものを選ぼう

出力例
main > ph 1000
[(3,4,5),(4,3,5)......

事前準備
まず、関数の型を宣言する

ph 	:: Int -> [(Int,Int,Int)]

これは、phという名前(::の左側に書いてある)が、関数であること(::の右側の->が関数の宣言であることを示している)、Int型の変数をひとつ引数にとり(->の左側が引数)、(Int,Int,Int)という整数3個の組を要素とするリストを返すこと(->の右側が返値の型)を現わしてる。

次に、関数本体を定義する。

ph n	= []

まず、一番左が定義する関数の名前、次のnが引数の名前、=の右側が返値の定義。とりあえず空リストを返しておく。この関数は何もしてくれないが、とりあえず動かしてみることはできる。

Prelude> ph 3
[]
Prelude>

1.phを実行した時に組み合わせを返せるようにしてみる

ph n 	= [(x,y,z)]

これは、x,y,zについて何も言及していないので実行することはできないけど、一応雛形となる。次に問題によれば、x,y,zはそれぞれ1以上n以下の整数なので、そのことをhaskellに教えてあげる。

ph n	= [(x,y,z)|x<-[1..n],y<-[1..n],z<-[1..n]]
の右側で、x,y,zがそれぞれどんな物かを教えてあげることにする。[1..n]は、1からnまでの整数を順番に生成してくれる、ジェネレータというもので、<-の記号により、生成した要素を順番にx,y,zに適用していってくれる。言葉で説明するのは解りにくいけど、実行してみれば、その意味はすぐ解る。

Prelude> ph 3
[(1,1,1),(1,1,2),(1,1,3),(1,2,1),(1,2,2),(1,2,3),(1,3,1),(1,3,2),(1,3,3),(2,1,1),(2,1,2),(2,1,3),(2,2,1),(2,2,2),(2,2,3),(2,3,1),(2,3,2),(2,3,3),(3,1,1),(3,1,2),(3,1,3),(3,2,1),(3,2,2),(3,2,3),(3,3,1),(3,3,2),(3,3,3)]
Prelude>

2.の部分
こうやって出力してくれるリストのうち、必要なのは式 x*x+y*y==z*zを満すものダケだということを、haskellに教えてあげる。

ph n	= [(x,y,z)|x<-[1..n],y<-[1..n],z<-[1..n],x*x+y*y==z*z]

,で区切って、条件式を書くだけでOKという簡明さ。これで完成。

Prelude> ph 10
[(3,4,5),(4,3,5),(6,8,10),(8,6,10)]
Prelude>

pythonは、haskellの良いところをパクってきているので、pythonでも同じくらい簡単に書ける。

def ph (n):
    return [(x,y,z) for x in range (1,n) for y in range(1,n) for z in range(1,n)
            if x*x+y*y == z*z]

プログラミングのことを知らない中学生でも理解できるのではないか、というくらい簡単な気がする