第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";
         }

参考:10分で学ぶHaskell - HaskellWiki

引数によるマッチング(パターンマッチング)

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"


と言う感じで高階関数受け取る関数を作ることができる。