Haskellの勉強 1日目
公開日:2015-01-03 更新日:2019-05-13
[Haskell]
はじめる
関数型プログラミング言語は全くやったことがないので、
Haskell(ハスケル)を少しだけ勉強してみることにしました。
Haskell(ハスケル)を少しだけ勉強してみることにしました。
Haskell のインストール
いま使っている OS が Windows7 64bit なので、
https://www.haskell.org/platform/windows.html から、64bit版の方をダウンロードしました。
ファイルをダウンロードしたら、ファイルを実行してインストールする。
適当に次へ次へでインストールしたら、
C:/Program Files/Haskell Platform にインストールされました。
https://www.haskell.org/platform/windows.html から、64bit版の方をダウンロードしました。
ファイルをダウンロードしたら、ファイルを実行してインストールする。
適当に次へ次へでインストールしたら、
C:/Program Files/Haskell Platform にインストールされました。
コンパイルと実行
Haskell で書かれたソースファイルを実行するには、コンパイルしてEXEファイルを作成する必要があります。
Haskellのインストールフォルダの bin 配下にある ghc.exe がコンパイラです。
具体的には、
C:/Program Files/Haskell Platform/2014.2.0.0/bin/ghc.exe
のような場所にあります。
これに引数としてソースファイルを渡すと、コンパイルしてくれます。
例えば、以下のファイルを作成して、
test.exe をコマンドプロンプト上で実行すると、
以下のような結果になります。
Haskellのインストールフォルダの bin 配下にある ghc.exe がコンパイラです。
具体的には、
C:/Program Files/Haskell Platform/2014.2.0.0/bin/ghc.exe
のような場所にあります。
これに引数としてソースファイルを渡すと、コンパイルしてくれます。
例えば、以下のファイルを作成して、
//test.hs
main = putStrLn "Hello"
コマンドプロンプトで、"C:/Program Files/Haskell Platform/2014.2.0.0/bin/ghc.exe" test.hs
のように実行すると、test.exe が作成されます。test.exe をコマンドプロンプト上で実行すると、
以下のような結果になります。
Hello
試行錯誤
以下はいろいろ試行錯誤した結果です。
まだはじめたばかりなので、誤解している点が結構あるかもしれませんのであしからず。
ソース、実行結果、説明の順で書いて行きます。
まだはじめたばかりなので、誤解している点が結構あるかもしれませんのであしからず。
ソース、実行結果、説明の順で書いて行きます。
main関数
main = do
putStrLn "Hello"
putStrLn "Hello"
Hello
Hello
C言語で言うところの、void main() {
printf("Hello\n");
printf("Hello\n");
}
と同じです。関数を定義する時は、代入するようです。
また、関数の処理が複数行になる場合は、do を付けます。
数値を文字列に変換する
main = putStrLn(show(123) ++ "4")
1234
数値を文字列を変換するには、show を使います。また、文字列結合には ++ を使います。
文字列を数値に変換する
main = putStrLn(show(read("123") + 1))
124
文字列を数値に変換するには、read を使います。上記のソースでは、putStrLn が文字列を受け取るため、show で数値から文字列に戻しています。
計算1
main = do
print (1 + 2)
--print 1 + 2 -- エラー
3
3行目は、print 1 の結果と 2 を加算することを意味するためエラーとなる。計算2
test a = a * 10
main = do
print (test 1 + 2)
print (test (1 + 2))
12
30
4行目は、test 1 の結果である 10 と 2 を足しているので 12 となる。5行目は、test 3 が実行されるため、30 となる。
計算3
main = do
print (1 + 2)
print ((1::Int) + (2::Int))
print ((1::Integer) + (2::Integer))
print ((1.23::Float) + (9.87::Float))
print ((1.23::Double) + (9.87::Double))
print (fromIntegral(2::Int) + (7::Int))
print (fromIntegral(2::Int) + (7::Integer))
print (fromIntegral(2::Integer) + (7::Int))
print (fromIntegral(2::Integer) + (7::Integer))
print (realToFrac(1.23::Float) + (9.87::Double))
--print ((1::Int) + (2::Integer)) -- エラー
--print ((1::Num) + (2::Num)) -- エラー
--print ((1::integral) + (9::integral)) -- エラー
--print ((1.23::Fractional) + (9.87::Fractional)) -- エラー
3
3
3
11.1
11.1
9
9
9
9
11.100000019073486
リテラル値(数値)に型を明示的に付けることができる。その場合、型が一致しないと計算できない。
型が一致しない場合は、fromIntegral や realToFrac を使用して、
Num や Fractional に型を変換すると計算できる。
関数合成
main = (putStrLn . show) 123
123
上記のソースは、putStrLn(show(123)) と同じ意味になります。関数1
test = (putStrLn . show)
main = test 123
123
関数合成した関数を test として定義しています。関数2
increment a = a + 1
main = (putStrLn . show . increment . increment . increment) 1
4
1を加算する関数 increment を定義しました。最初の increment a は、関数名が increment、引数が1つあることを意味します。
= の右が処理内容になります。今回は1つ加算なので、a + 1 としています。
関数3
add a b = a + b
main = (putStrLn . show)(add 1 2)
3
渡された2つの引数を加算するだけの関数です。最初の add a b は、関数名が add、引数が2つあることを意味します。
= の右が処理内容になります。今回は加算なので、a + b としています。
関数を使用する時は、add 1 2 のようにします。
add(1, 2) のようにして、括弧やカンマは付けない。
以下の書き方は全てエラーになります。
main = (putStrLn . show . add)(1 2) -- エラー
main = (putStrLn . show)add 1 2 -- エラー
main = (putStrLn . show)add(1, 2) -- エラー
main = (putStrLn . show)add(1 2) -- エラー
main = (putStrLn . show)(add(1 2)) -- エラー
main = (putStrLn . show)(add(1, 2)) -- エラー
1行目の関数合成を使った書き方は、問題なさそうなのですが、引数が一致していないせいかエラーになる。
関数4
inc :: String -> Integer
inc a = (read a) + 1
add :: Integer -> String -> Integer
add a b = a + (read b)
main = do
(putStrLn . show . inc) "1"
(putStrLn . show)(add 1 "2")
2
3
型を明確に定義することもできる。1行目は、Stringの引数を受け取り、Integerを返すことを意味します。
4行目は、1番目の引数に Integer、2番目の引数に Stringを受け取り、Integerを返すことを意味します。
この書き方、すごくわかりづらい。。。
関数5
:type fromIntegral
fromIntegral :: (Num b, Integral a) => a -> b
Haskell の bin 配下の ghci.exe を実行するか、Haskell のソースファイル(例:test.hs) をダブルクリックすると、
コマンドが入力できる画面が出ます。
ここで、:type fromIntegral と入力すると、定義がわかります。:t でも可。
また、:info、:i でも、定義が表示されます。
fromIntegral の定義は、Integral の引数を受け取り、Num を返すことがわかります。
=> の左側に、引数の型を定義して、
=> の右側に、引数と戻り値を定義する。
ちなみに、引数の型は、引数の数だけ定義する必要はない。
関数6
test a b c = show (read (show (a + b + c)) :: Int)
main = print (test 1 2 3)
"6"
関数の型を定義せずにコンパイルして、ソースファイルをダブルクリックして ghci.exe を起動し、
:t type とすると、
test :: (Show a, Num a) => a -> a -> a -> String
と、自動的に定義された内容がわかる。test と言う関数名で、
Num の引数を3つ取り、String を返す関数であることがわかる。
問題は、Show a。
Num と同じく a になっているので、必要なのかどうか。
関数の定義をソースファイルに追加して試行錯誤してみる。
test :: (Show a, Num a) => a -> a -> a -> String
test a b c = show (read (show (a + b + c)) :: Int)
main = print (test 1 2 3)
そのまま実行すると動くので、試しに、Show a や Num a を削除してみる。
削除すると、エラーになるので、Show a は必要であることがわかる。
ただ、read も使っているけど、read は定義していない。
試しに、Read a を追加してみる。
test :: (Show a, Num a, Read a) => a -> a -> a -> String
test a b c = show (read (show (a + b + c)) :: Int)
main = print (test 1 2 3)
実行すると動く。read は引数が文字列なので省略可能、
show は show の引数の型指定で必要、
Num は a + b + c の演算で必要、なのかな。
試しに + の演算を削除して、:type で定義を確認してみる。
test a = show (read (show (a)) :: Int)
main = print (test 1)
test :: Show a => a -> String
予想通り、Num a が消えた。関数7
test 0 = 100
test 1 = 200
test x = x + 1
main = do
print (test 0)
print (test 1)
print (test 2)
print (test 3)
100
200
3
4
関数は条件毎に式を定義できる。0 の場合は 100 を返し、
1 の場合は 200 を返し、
それ以外の場合は、引数の値に 1 を加算して返す。
x と言う変数名を使っているが、何を使っても問題ない。
また、上から評価しているので、x = x + 1 を先に定義すると、動作が変わる。
test xxx = xxx + 1
test 0 = 100
test 1 = 200
main = do
print (test 0)
print (test 1)
print (test 2)
print (test 3)
1
2
3
上記の場合、先に xxx で全てのケースの値を受け取ってしまうので、その後の 0 の場合、1 の場合は、処理されない。
関数8
test a
| a == 0 = 123
| a > 0 = a + 1
| otherwise = -999
main = do
print (test 0)
print (test 1)
print (test(-1))
##src|123
2
-999
##/e##
このように書くこともできる。ちなみに、-1 を引数で渡す時には、括弧で囲わないとエラーになる。
test - 1 と分解されて、- を引数として認識するため?
ここまでの感想
コンパイルエラー出まくりで、頭が爆発しそう。。。。。。
特に Haskell の構文が難しい。
Wikipedia に初歩的なことが書いてあるので、
まずはそれを見た方が良さそうかな。
とりあえず現時点では、
Haskell でプログラムを書けるようになる自信が全くない。。。
特に Haskell の構文が難しい。
Wikipedia に初歩的なことが書いてあるので、
まずはそれを見た方が良さそうかな。
とりあえず現時点では、
Haskell でプログラムを書けるようになる自信が全くない。。。