第39回 Haskellちょっとだけ入門
Haskellをちょっとだけ入門してみよう。
インタプリタ
インタプリタの実行は、
$ ghci
である。
インタプリタ内では、:で始まる内容はHaskellプログラムではなく、ghci自身の操作のようだ。
:help ヘルプの表示 :quite ghciの終了
main関数を書いてみよう
main = putStr "HELLO"
実行は上のソースを h.hs という感じで適当な名前をつけて、
$ runghc h.hs
でできるみたい。
結果:HELLO
参考:10分で学ぶHaskell - HaskellWiki
Haskell基礎文法最速マスター - think and error
関数を作ってみよう
increment x = x + 1 main = print( increment 3 )
結果:4
ちなみに print は引数になんでも受け取れるが、 putStrはStringしか受け取れないみたいで以下はエラー
increment x = x + 1 main = putStr( increment 3 )
次だとOK
s = "HELLO" main = putStr s
結果:HELLO
複数の出力をしたいとき
print をたくさん使って、出力をいっぱい出したいときは、do{ } というブロックで囲まないといけないみたい。
main = do{ print "AAA"; print "BBB"; print "CCC"; }
引数によるマッチング(パターンマッチング)
Haskellでは同名の関数を作る事ができる(型が同じ場合)。
ではどちらの関数が呼ばれるのだろうか?それは引数から判断される。
hoge :: Int -> String -- 型指定 hoge 0 = "ZERO" -- 引数が0のときは"ZERO"を返す hoge 1 = "ONE" -- 引数が1のときは"ONE"を返す hoge x = "NOT ZERO OR ONE" -- 引数が上記以外のときは"NOT ZERO OR ONE"を返す main = do{ print( hoge 0 ); print( hoge 1 ); print( hoge 2 ); }
なお同じ事は、ガードと呼ばれる記法によっても書けるみたい。
参考:プログラミング/Haskell/入門 - Flightless wing
特定の関数以外から関数を呼ばれたくないとき
グローバルなところに関数を書いちゃうと、いろんなところから呼ばれてしまう。
これはやっかいだ。ということからどこかの言語でprivateが導入された。
こんな感じで、Haskellにも関数をいろんなところから呼ばせない仕組みがあるみたい。
方法は2通り。
let プライベートな関数はここに書く in ここでしかプライベートな関数呼べない
と
ここでしかプライベートな関数呼べない where プライベートな関数はここに書く
具体的なソースで示すと、
hoge = let foo x = "HELLO" ++ " " ++ x in foo "WORLD" main = do{ print hoge }
結果:"HELLO WORLD"
hoge = foo "WORLD" where foo x = "HELLO" ++ " " ++ x main = do{ print hoge }
結果:"HELLO WORLD"
※ちなみに ++ は文字列連結の演算子みたい
参考:プログラミング/Haskell/入門 - Flightless wing
関数がリストを受け取るときの特殊な書き方
なんか関数がリストを受け取るときは、リストの先頭とそれ以外をわけて受け取れる書き方があるみたい。
hoge(先頭を受け取る変数名:先頭以外を受け取る変数名) = 関数の内容
で書けるみたい。よく x:xs っという形で書かれるけど、今試してみたところ別に名前は xとxsでなくてもよいみたい。
hoge (x:xs) = x foo (x:xs) = xs bar (d:ds) = d piyo (z:y) = y main = do{ print( hoge [1..3] ); print( foo [1..3] ); print( bar [1..3] ); print( piyo [1..3] ); }
結果:
1
[2,3]
1
[2,3]
リストの先頭に値をいれる
リストの先頭に値をいれるときは、
:
という記号でできるみたい。
ちなみに型を定義するときの記号は
::
と:が2つなので見間違わないようにしよう。
hoge(x:xs) = 6 : xs main = do { print( 9 : [1..3]); -- [9,1,2,3] print( 9 : 8 : [1..3]); -- [9,8,1,2,3] print( 9 : [8,7] ++ [1..3]); -- [9,8,7,1,2,3] print( hoge [1..3]); -- [6,2,3] }
高階関数を受け取る関数の作り方
高階関数をつくる関数を定義する方法。
型の指定は次のように書ける。
今回作る関数名 :: ( 高階関数の型 ) -> 今回作る関数の引数 -> 今回作る関数が返す値の型
つまり、こういうことである。
名称 | 意味 |
---|---|
関数名 | 今回作る関数名 |
第1引数 | ( 高階関数の型 ) |
第2引数 | 今回作る関数の引数 |
戻り値 | 今回作る関数が返す値の型 |
はやい話が、高階関数を受け取る部分は( )で囲えばよい。
具体例でみると、
hoge :: ( String -> String ) -> String -> String hoge g s = g(s)
こんな感じである。ちなみに型推論があるので
hoge g s = g(s)
だけでもうまくいくようである。
高階関数を使ったソース
高階関数を使ったソースを掲示する。
あくまで、関数に関数が渡せるところだけに集中しているので、高階関数を使うことで利益がでているようなソースではないことに注意されたい。
----------------------------------------------------- -- 高階関数受け取る関数 ----------------------------------------------------- hoge :: ( String -> String ) -> String -> String hoge g s = g(s) ---------------------------------------------------- -- hogeに与える高階関数 ---------------------------------------------------- f :: String -> String f str = "HELLO" ++ " " ++ str ---------------------------------------------------- -- main ---------------------------------------------------- main = do { print( hoge f "WORLD") }
結果:"HELLO WORLD"
無名関数
高階関数が渡せるようになったのならば、いちいち渡す関数に名前を付けたくないのが人情というものである。
Haskellの無名関数は、
\引数名 -> 関数の内容
で書けるみたい。無名ではない普通の関数を書く時は -> ではなく = だったのにどうして -> になってしまったのだろう。
よくわからないが = ではなく、 -> であることに注意すれば比較的に簡単に書ける。
それでは、先ほどのソースからfという関数名は余計なので、無名関数で与えるソースを書いてみる。
----------------------------------------------------- -- 高階関数受け取る関数 ----------------------------------------------------- hoge :: ( String -> String ) -> String -> String hoge g s = g(s) ---------------------------------------------------- -- main ---------------------------------------------------- main = do { print( hoge (\str -> "HELLO" ++ " " ++ str) "WORLD") }
結果:"HELLO WORLD"
次の部分が無名関数を書いたところである。
(\str -> "HELLO" ++ " " ++ str)
先のソースだと、どこまでが無名関数の内容かHaskellがわからなかったようなので( )で無名関数をくくることで成功した。
型推論があるので型は書かなくてよいよね
型推論があるので型を書かなくてよい場合がある。
hoge :: ( String -> String ) -> String -> String
とか正直読み辛い気がするので、書かなくてよいや。
なんてことで、先ほどの無名関数を使ったソースからhogeの型指定の部分のところを消してやって
hoge g s = g(s) main = do { print( hoge (\str -> "HELLO" ++ " " ++ str) "WORLD") }
結果:"HELLO WORLD"
と言う感じで高階関数受け取る関数を作ることができる。