Difference between revisions of "10分で学ぶHaskell"
Ymotongpoo (talk | contribs) |
(→構造化されたデータ: 訳の修正とコードの整形) |
||
(3 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
最も普及しているHaskellのコンパイラは [[GHC]] です。GHCは http://www.haskell.org/ghc/download.html からダウンロードできます。GHCのバイナリは [[GNU/Linux]]、[[BSD | FreeBSD]]、[[Mac OS X | MacOS]]、[[Windows]]、[[Solaris]]で動作します。[[GHC]]をインストールすると、<tt>ghc</tt>と<tt>[[GHC/GHCi | ghci]]</tt>という2つのプログラムが入っているのが確認できます。最初の<tt>ghc</tt>の方はHaskellのライブラリやアプリケーションをバイナリコードにコンパイルします。<tt>[[GHC/GHCi | ghci]]</tt>はインタプリタで、Haskellコードを書いてすぐに結果を得ることができる環境です。 | 最も普及しているHaskellのコンパイラは [[GHC]] です。GHCは http://www.haskell.org/ghc/download.html からダウンロードできます。GHCのバイナリは [[GNU/Linux]]、[[BSD | FreeBSD]]、[[Mac OS X | MacOS]]、[[Windows]]、[[Solaris]]で動作します。[[GHC]]をインストールすると、<tt>ghc</tt>と<tt>[[GHC/GHCi | ghci]]</tt>という2つのプログラムが入っているのが確認できます。最初の<tt>ghc</tt>の方はHaskellのライブラリやアプリケーションをバイナリコードにコンパイルします。<tt>[[GHC/GHCi | ghci]]</tt>はインタプリタで、Haskellコードを書いてすぐに結果を得ることができる環境です。 | ||
− | == | + | == 簡単な数式 == |
− | + | たいていの数式は直接<tt>ghci</tt>に入力して計算結果を得ることができます。<tt>Prelude></tt>はGHCiのデフォルトのプロンプトです。 | |
− | + | <tt> | |
− | + | :Prelude> <hask>3 * 5</hask> | |
− | + | :15 | |
− | + | :Prelude> <hask>4 ^ 2 - 1</hask> | |
− | + | :15 | |
− | + | :Prelude> <hask>(1 - 5)^(3 * 2 - 4)</hask> | |
+ | :16 | ||
+ | </tt> | ||
− | + | 文字列はダブルクォート(二重引用符)で囲みます。文字列の結合をするときは<hask>++</hask>を使います。 | |
− | + | <tt> | |
− | + | :Prelude> <hask>"Hello"</hask> | |
− | + | :"Hello" | |
− | + | :Prelude> <hask>"Hello" ++ ", Haskell"</hask> | |
+ | :"Hello, Haskell" | ||
+ | </tt> | ||
[[関数]]を呼び出すときは関数の後に直接引数を並べて行います。関数呼び出しに括弧は必要ありません。こんな感じです: | [[関数]]を呼び出すときは関数の後に直接引数を並べて行います。関数呼び出しに括弧は必要ありません。こんな感じです: | ||
− | + | <tt> | |
− | + | :Prelude> <hask>succ 5</hask> | |
− | + | :6 | |
− | + | :Prelude> <hask>truncate 6.59</hask> | |
− | + | :6 | |
− | + | :Prelude> <hask>round 6.59</hask> | |
− | + | :7 | |
− | + | :Prelude> <hask>sqrt 2</hask> | |
− | + | :1.4142135623730951 | |
− | + | :Prelude> <hask>not (5 < 3)</hask> | |
− | + | :True | |
− | + | :Prelude> <hask>gcd 21 14</hask> | |
+ | :7 | ||
+ | </tt> | ||
== コンソール == | == コンソール == | ||
− | [[IO入門編|I/Oアクション]] | + | [[IO入門編|I/Oアクション]]はコンソールからの入出力を行うのに使います。よくある例は: |
− | + | <tt> | |
− | + | :Prelude> <hask>putStrLn "Hello, Haskell"</hask> | |
− | + | :Hello, Haskell | |
− | + | :Prelude> <hask>putStr "No newline"</hask> | |
− | + | :No newlinePrelude> <hask>print (5 + 4)</hask> | |
− | + | :9 | |
− | + | :Prelude> <hask>print (1 < 2)</hask> | |
+ | :True | ||
+ | </tt> | ||
<hask>putStr</hask>関数 と <hask>putStrLn</hask>関数 は文字列をターミナルに出力します。<hask>print</hask>関数はどんな型の値でも出力します。(文字列を<hask>print</hask>するときは、引用符が前後につきます) | <hask>putStr</hask>関数 と <hask>putStrLn</hask>関数 は文字列をターミナルに出力します。<hask>print</hask>関数はどんな型の値でも出力します。(文字列を<hask>print</hask>するときは、引用符が前後につきます) | ||
− | + | ひとつの式の中で複数の I/O アクションを使いたい場合は、<hask>do</hask>ブロックを使います。アクションはセミコロンで区切られます。 | |
− | + | <tt> | |
− | + | :Prelude> <hask>do { putStr "2 + 2 = " ; print (2 + 2) }</hask> | |
− | + | :2 + 2 = 4 | |
− | + | :Prelude> <hask>do { putStrLn "ABCDE" ; putStrLn "12345" }</hask> | |
− | + | :ABCDE | |
+ | :12345 | ||
+ | </tt> | ||
− | 読み込みは <hask>getLine</hask>(<hask>String</hask>を返します)か<hask>readLn</hask>(どんな型の値でも返します)でできます。<hask> <- </hask> | + | 読み込みは <hask>getLine</hask>(<hask>String</hask>を返します)か<hask>readLn</hask>(どんな型の値でも返します)でできます。<hask> <- </hask>という記号はI/Oアクションの結果に名前を付けるために使います。 |
− | + | <tt> | |
− | + | :Prelude> <hask>do { n <- readLn ; print (n^2) }</hask> | |
− | + | :4 | |
+ | :16 | ||
+ | </tt> | ||
(4が入力で16が結果です。) | (4が入力で16が結果です。) | ||
− | + | 別の書き方で<hask>do</hask>ブロックを書くこともできます。中括弧やセミコロンを使わずに、インデントでブロックと式の区切りを表します。<tt>ghci</tt>では書けないので、ソースファイル(例えば<tt>Test.hs</tt>)に書いてビルドしてみましょう。 | |
<haskell> | <haskell> | ||
Line 78: | Line 90: | ||
</haskell> | </haskell> | ||
− | ビルドは <tt>ghc --make Test.hs</tt> でできます。実行ファイル <tt>Test</tt> ができるはずです。([[Windows]]では<tt>Test.exe</tt>となります)ここで<hask>if</hask> | + | ビルドは <tt>ghc --make Test.hs</tt> でできます。実行ファイル <tt>Test</tt> ができるはずです。([[Windows]]では<tt>Test.exe</tt>となります)ここで<hask>if</hask>式も一緒に習得できてしまいますね。 |
− | <hask>do</hask>の直後の最初の空白でない文字は特別な文字です。上の例の場合では<hask>putStrLn</hask>の先頭の<tt>p</tt>がそれです。<hask>do</hask>ブロック内のすべての行はその<hask>p</hask> | + | <hask>do</hask>の直後の最初の空白でない文字は特別な文字です。上の例の場合では<hask>putStrLn</hask>の先頭の<tt>p</tt>がそれです。<hask>do</hask>ブロック内のすべての行はその<hask>p</hask>と同じ列から開始します。もしインデントがより深ければ、それ以前の行の一部となります。逆にインデントが浅ければ、<hask>do</hask>ブロックはそこで終わります。これは“レイアウト”と呼ばれ、Haskellでは、いつも行の区切り文字や中括弧を使うのは面倒なので、代わりにレイアウトを使います。(<hask>then</hask>句と<hask>else</hask>句はこれが理由で<hask>if</hask>よりインデントが深くなければならないのです。もし<hask>if</hask>と同じ列から開始した場合、<hask>if</hask>と別の行と解釈されてしまって、エラーとなります。) |
− | (Note: レイアウトを使う場合はインデントにタブを'''用いないでください''' | + | (Note: レイアウトを使う場合はインデントにタブを'''用いないでください'''。技術的にはタブが8文字であれば動作しますが、おすすめできません。また中には使う人もいるかもしれませんが、プロポーショナルフォントを使うこともおすすめしません。 |
− | == | + | == 簡単な型 == |
− | これまで、[[型]] | + | これまで、[[型]]宣言に関して全く触れてきませんでした。それはHaskellが型推論を行うからです。一般的に型宣言はしたい場合以外はする必要がありません。型宣言をする場合は<hask>::</hask>を使って行います。 |
− | + | <tt> | |
− | + | :Prelude> <hask>5 :: Int</hask> | |
− | + | :5 | |
− | + | :Prelude> <hask>5 :: Double</hask> | |
+ | :5.0 | ||
+ | </tt> | ||
− | Haskellでは[[型]] (そして後で触れる型[[ | + | Haskellでは[[型]] (そして後で触れる型[[クラス]])は常に大文字から開始します。変数は常に小文字から始まります。これは言語仕様であり、命名規則([[Studly capitals|naming convention]])ではありません。 |
<tt>ghci</tt>を使って、ある値がどんな型かを確認することもできます。普段、自分では型宣言をしないので、この機能は役に立ちます。 | <tt>ghci</tt>を使って、ある値がどんな型かを確認することもできます。普段、自分では型宣言をしないので、この機能は役に立ちます。 | ||
Line 108: | Line 122: | ||
数字に関してはもっと面白いものが見られます。 | 数字に関してはもっと面白いものが見られます。 | ||
− | + | <tt> | |
− | + | :Prelude> :t <hask>42</hask> | |
− | + | :<hask>42 :: (Num t) => t</hask> | |
− | + | :Prelude> :t <hask>42.0</hask> | |
− | + | :<hask>42.0 :: (Fractional t) => t</hask> | |
− | + | :Prelude> :t <hask>gcd 15 20</hask> | |
+ | :<hask>gcd 15 20 :: (Integral t) => t</hask> | ||
+ | </tt> | ||
− | + | これらの型は“型クラス”を用いています。つまり: | |
− | * <hask>42</hask> | + | * <hask>42</hask> はあらゆる数値型として使うことができます。(このおかげで<hask>5</hask>を<hask>Int</hask>としても<hask>Double</hask>としても宣言できたわけです) |
− | * <hask>42.0</hask> | + | * <hask>42.0</hask> はどのような小数型にもなれますが、整数型にはなれません。 |
− | * <hask>gcd 15 20</hask> | + | * <hask>gcd 15 20</hask> (ちなみにこれは関数呼び出しです)はどのような整数型にもなれますが、小数型にはなれません。 |
− | + | Haskellの“Prelude”(標準ライブラリのうち初期状態でimportされているモジュール)内には5種類の数値型があります。 | |
* <hask>Int</hask> は少なくとも30ビットの精度の整数です | * <hask>Int</hask> は少なくとも30ビットの精度の整数です | ||
* <hask>Integer</hask> は精度無制限の整数型です | * <hask>Integer</hask> は精度無制限の整数型です | ||
* <hask>Float</hask> は単精度浮動小数点数です | * <hask>Float</hask> は単精度浮動小数点数です | ||
− | * <hask>Double</hask> | + | * <hask>Double</hask> は倍精度浮動小数点数です |
* <hask>Rational</hask> は分数型で丸め誤差がありません | * <hask>Rational</hask> は分数型で丸め誤差がありません | ||
Line 133: | Line 149: | ||
ごちゃまぜにしてみましょう。 | ごちゃまぜにしてみましょう。 | ||
− | + | <tt> | |
− | + | :Prelude> <hask>gcd 42 35 :: Int</hask> | |
− | + | :7 | |
− | + | :Prelude> <hask>gcd 42 35 :: Double</hask> | |
− | + | : | |
− | + | :<interactive>:1:0: | |
+ | : No instance for (Integral Double) | ||
+ | </tt> | ||
最後に触れておいたほうがよい型は <hask>()</hask>で表されるもので "Unit" と呼ばれます。この型はたった一つの値をとり、やはり<hask>()</hask>と書かれ "Unit" と呼ばれます。 | 最後に触れておいたほうがよい型は <hask>()</hask>で表されるもので "Unit" と呼ばれます。この型はたった一つの値をとり、やはり<hask>()</hask>と書かれ "Unit" と呼ばれます。 | ||
− | + | <tt> | |
− | + | :Prelude> <hask>()</hask> | |
− | + | :<hask>()</hask> | |
− | + | :Prelude> :t <hask>()</hask> | |
+ | :<hask>() :: ()</hask> | ||
+ | </tt> | ||
これはC言語系の言語での<tt>void</tt>キーワードに似ています。もしなにも返り値を期待しない場合、I/Oアクションから<hask>()</hask>を返すことができます。 | これはC言語系の言語での<tt>void</tt>キーワードに似ています。もしなにも返り値を期待しない場合、I/Oアクションから<hask>()</hask>を返すことができます。 | ||
Line 151: | Line 171: | ||
== 構造化されたデータ == | == 構造化されたデータ == | ||
− | + | 基本データ型は2つの方法で容易にまとめることができます。一つはリストで角括弧 [ ] で囲います。もう一つはタプルで丸括弧 ( ) で囲みます。 | |
リストは同じ型の複数の値を保持する場合に使われます。 | リストは同じ型の複数の値を保持する場合に使われます。 | ||
− | + | <tt> | |
− | + | :Prelude> <hask>[1, 2, 3]</hask> | |
− | + | :[1,2,3] | |
− | + | :Prelude> <hask>[1 .. 5]</hask> | |
− | + | :[1,2,3,4,5] | |
− | + | :Prelude> <hask>[1, 3 .. 10]</hask> | |
− | + | :[1,3,5,7,9] | |
− | + | :Prelude> <hask>[True, False, True]</hask> | |
+ | :[True,False,True] | ||
+ | </tt> | ||
文字列は単に文字のリストにすぎません。 | 文字列は単に文字のリストにすぎません。 | ||
− | + | <tt> | |
− | + | :Prelude> <hask>['H', 'e', 'l', 'l', 'o']</hask> | |
+ | :"Hello" | ||
+ | </tt> | ||
− | <hask>:</hask>演算子は要素をリストの先頭に追加します。(Lisp系言語でいうところの<tt>cons</tt> | + | <hask>:</hask>演算子は要素をリストの先頭に追加します。(Lisp系言語でいうところの<tt>cons</tt>関数です) |
− | + | <tt> | |
− | + | :Prelude> <hask>'C' : ['H', 'e', 'l', 'l', 'o']</hask> | |
+ | :"CHello" | ||
+ | </tt> | ||
タプルは決まった数の値を保持できます。ただしそれぞれの型は異なっていても構いません。 | タプルは決まった数の値を保持できます。ただしそれぞれの型は異なっていても構いません。 | ||
− | + | <tt> | |
− | + | :Prelude> <hask>(1, True)</hask> | |
− | + | :(1,True) | |
− | + | :Prelude> <hask>zip [1 .. 5] ['a' .. 'e']</hask> | |
+ | :[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')] | ||
+ | </tt> | ||
− | この例の最後で<hask>zip</hask>が使われています。これは2つのリストを1つのタプルのリストにするライブラリ関数です。 | + | この例の最後で <hask>zip</hask>関数 が使われています。これは2つのリストを1つのタプルのリストにするライブラリ関数です。 |
型は予想どおり以下のようになります。 | 型は予想どおり以下のようになります。 | ||
− | + | <tt> | |
− | + | :Prelude> :t <hask>['a' .. 'c']</hask> | |
− | + | :<hask>['a' .. 'c'] :: [Char]</hask> | |
− | + | :Prelude> :t <hask>[('x', True), ('y', False)]</hask> | |
+ | :<hask>[('x', True), ('y', False)] :: [(Char, Bool)]</hask> | ||
+ | </tt> | ||
リストはHaskellではとてもよく使われます。リストを操作する便利な関数がいくつもあります。 | リストはHaskellではとてもよく使われます。リストを操作する便利な関数がいくつもあります。 | ||
− | + | <tt> | |
− | + | :Prelude> <hask>[1 .. 5]</hask> | |
− | + | :<hask>[1,2,3,4,5]</hask> | |
− | + | :Prelude> <hask>map (+ 2) [1 .. 5]</hask> | |
− | + | :<hask>[3,4,5,6,7]</hask> | |
− | + | :Prelude> <hask>filter (> 2) [1 .. 5]</hask> | |
+ | :<hask>[3,4,5]</hask> | ||
+ | </tt> | ||
また整列されたペア(2つの要素を持つタプル)に対する便利な関数が2つあります。 | また整列されたペア(2つの要素を持つタプル)に対する便利な関数が2つあります。 | ||
− | + | <tt> | |
− | + | :Prelude> <hask>fst (1, 2)</hask> | |
− | + | :<hask>1</hask> | |
− | + | :Prelude> <hask>snd (1, 2)</hask> | |
− | + | :<hask>2</hask> | |
− | + | :Prelude> <hask>map fst [(1, 2), (3, 4), (5, 6)]</hask> | |
+ | :<hask>[1,3,5]</hask> | ||
+ | </tt> | ||
[[how to work on lists]]も参照してください。 | [[how to work on lists]]も参照してください。 |
Latest revision as of 15:14, 28 March 2012
概要
Haskellは関数型で(つまりすべてが関数呼びだしで処理される)、静的な暗黙的型付けで(型はコンパイラによって確認され、明示的に宣言する必要はない)、遅延評価(必要となるまで処理されない)の言語です。系統が近い言語として最も人気のあるのはおそらくML系の言語でしょう。(MLは遅延評価ではないですが)
最も普及しているHaskellのコンパイラは GHC です。GHCは http://www.haskell.org/ghc/download.html からダウンロードできます。GHCのバイナリは GNU/Linux、 FreeBSD、 MacOS、Windows、Solarisで動作します。GHCをインストールすると、ghcと ghciという2つのプログラムが入っているのが確認できます。最初のghcの方はHaskellのライブラリやアプリケーションをバイナリコードにコンパイルします。 ghciはインタプリタで、Haskellコードを書いてすぐに結果を得ることができる環境です。
簡単な数式
たいていの数式は直接ghciに入力して計算結果を得ることができます。Prelude>はGHCiのデフォルトのプロンプトです。
- Prelude>
3 * 5
- 15
- Prelude>
4 ^ 2 - 1
- 15
- Prelude>
(1 - 5)^(3 * 2 - 4)
- 16
文字列はダブルクォート(二重引用符)で囲みます。文字列の結合をするときは++
を使います。
- Prelude>
"Hello"
- "Hello"
- Prelude>
"Hello" ++ ", Haskell"
- "Hello, Haskell"
関数を呼び出すときは関数の後に直接引数を並べて行います。関数呼び出しに括弧は必要ありません。こんな感じです:
- Prelude>
succ 5
- 6
- Prelude>
truncate 6.59
- 6
- Prelude>
round 6.59
- 7
- Prelude>
sqrt 2
- 1.4142135623730951
- Prelude>
not (5 < 3)
- True
- Prelude>
gcd 21 14
- 7
コンソール
I/Oアクションはコンソールからの入出力を行うのに使います。よくある例は:
- Prelude>
putStrLn "Hello, Haskell"
- Hello, Haskell
- Prelude>
putStr "No newline"
- No newlinePrelude>
print (5 + 4)
- 9
- Prelude>
print (1 < 2)
- True
putStr
関数 と putStrLn
関数 は文字列をターミナルに出力します。print
関数はどんな型の値でも出力します。(文字列をprint
するときは、引用符が前後につきます)
ひとつの式の中で複数の I/O アクションを使いたい場合は、do
ブロックを使います。アクションはセミコロンで区切られます。
- Prelude>
do { putStr "2 + 2 = " ; print (2 + 2) }
- 2 + 2 = 4
- Prelude>
do { putStrLn "ABCDE" ; putStrLn "12345" }
- ABCDE
- 12345
読み込みは getLine
(String
を返します)かreadLn
(どんな型の値でも返します)でできます。<-
という記号はI/Oアクションの結果に名前を付けるために使います。
- Prelude>
do { n <- readLn ; print (n^2) }
- 4
- 16
(4が入力で16が結果です。)
別の書き方でdo
ブロックを書くこともできます。中括弧やセミコロンを使わずに、インデントでブロックと式の区切りを表します。ghciでは書けないので、ソースファイル(例えばTest.hs)に書いてビルドしてみましょう。
main = do putStrLn "What is 2 + 2?"
x <- readLn
if x == 4
then putStrLn "You're right!"
else putStrLn "You're wrong!"
ビルドは ghc --make Test.hs でできます。実行ファイル Test ができるはずです。(WindowsではTest.exeとなります)ここでif
式も一緒に習得できてしまいますね。
do
の直後の最初の空白でない文字は特別な文字です。上の例の場合ではputStrLn
の先頭のpがそれです。do
ブロック内のすべての行はそのp
と同じ列から開始します。もしインデントがより深ければ、それ以前の行の一部となります。逆にインデントが浅ければ、do
ブロックはそこで終わります。これは“レイアウト”と呼ばれ、Haskellでは、いつも行の区切り文字や中括弧を使うのは面倒なので、代わりにレイアウトを使います。(then
句とelse
句はこれが理由でif
よりインデントが深くなければならないのです。もしif
と同じ列から開始した場合、if
と別の行と解釈されてしまって、エラーとなります。)
(Note: レイアウトを使う場合はインデントにタブを用いないでください。技術的にはタブが8文字であれば動作しますが、おすすめできません。また中には使う人もいるかもしれませんが、プロポーショナルフォントを使うこともおすすめしません。
簡単な型
これまで、型宣言に関して全く触れてきませんでした。それはHaskellが型推論を行うからです。一般的に型宣言はしたい場合以外はする必要がありません。型宣言をする場合は::
を使って行います。
- Prelude>
5 :: Int
- 5
- Prelude>
5 :: Double
- 5.0
Haskellでは型 (そして後で触れる型クラス)は常に大文字から開始します。変数は常に小文字から始まります。これは言語仕様であり、命名規則(naming convention)ではありません。
ghciを使って、ある値がどんな型かを確認することもできます。普段、自分では型宣言をしないので、この機能は役に立ちます。
Prelude> :tTrue
True :: Bool
Prelude> :t'X'
'X' :: Char
Prelude> :t"Hello, Haskell"
"Hello, Haskell" :: [Char]
(上の例を見て気づいたように、[Char]
はString
の別名です。あとでをsection on listsを見てみましょう)
数字に関してはもっと面白いものが見られます。
- Prelude> :t
42
42 :: (Num t) => t
- Prelude> :t
42.0
42.0 :: (Fractional t) => t
- Prelude> :t
gcd 15 20
gcd 15 20 :: (Integral t) => t
これらの型は“型クラス”を用いています。つまり:
42
はあらゆる数値型として使うことができます。(このおかげで5
をInt
としてもDouble
としても宣言できたわけです)42.0
はどのような小数型にもなれますが、整数型にはなれません。gcd 15 20
(ちなみにこれは関数呼び出しです)はどのような整数型にもなれますが、小数型にはなれません。
Haskellの“Prelude”(標準ライブラリのうち初期状態でimportされているモジュール)内には5種類の数値型があります。
Int
は少なくとも30ビットの精度の整数ですInteger
は精度無制限の整数型ですFloat
は単精度浮動小数点数ですDouble
は倍精度浮動小数点数ですRational
は分数型で丸め誤差がありません
これら5つはすべて Num
型クラスの インスタンス です。最初の2つはIntegral
の インスタンス で、残り3つはFractional
の インスタンス です。
ごちゃまぜにしてみましょう。
- Prelude>
gcd 42 35 :: Int
- 7
- Prelude>
gcd 42 35 :: Double
- <interactive>:1:0:
- No instance for (Integral Double)
最後に触れておいたほうがよい型は ()
で表されるもので "Unit" と呼ばれます。この型はたった一つの値をとり、やはり()
と書かれ "Unit" と呼ばれます。
- Prelude>
()
()
- Prelude> :t
()
() :: ()
これはC言語系の言語でのvoidキーワードに似ています。もしなにも返り値を期待しない場合、I/Oアクションから()
を返すことができます。
構造化されたデータ
基本データ型は2つの方法で容易にまとめることができます。一つはリストで角括弧 [ ] で囲います。もう一つはタプルで丸括弧 ( ) で囲みます。
リストは同じ型の複数の値を保持する場合に使われます。
- Prelude>
[1, 2, 3]
- [1,2,3]
- Prelude>
[1 .. 5]
- [1,2,3,4,5]
- Prelude>
[1, 3 .. 10]
- [1,3,5,7,9]
- Prelude>
[True, False, True]
- [True,False,True]
文字列は単に文字のリストにすぎません。
- Prelude>
['H', 'e', 'l', 'l', 'o']
- "Hello"
:
演算子は要素をリストの先頭に追加します。(Lisp系言語でいうところのcons関数です)
- Prelude>
'C' : ['H', 'e', 'l', 'l', 'o']
- "CHello"
タプルは決まった数の値を保持できます。ただしそれぞれの型は異なっていても構いません。
- Prelude>
(1, True)
- (1,True)
- Prelude>
zip [1 .. 5] ['a' .. 'e']
- [(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')]
この例の最後で zip
関数 が使われています。これは2つのリストを1つのタプルのリストにするライブラリ関数です。
型は予想どおり以下のようになります。
- Prelude> :t
['a' .. 'c']
['a' .. 'c'] :: [Char]
- Prelude> :t
[('x', True), ('y', False)]
[('x', True), ('y', False)] :: [(Char, Bool)]
リストはHaskellではとてもよく使われます。リストを操作する便利な関数がいくつもあります。
- Prelude>
[1 .. 5]
[1,2,3,4,5]
- Prelude>
map (+ 2) [1 .. 5]
[3,4,5,6,7]
- Prelude>
filter (> 2) [1 .. 5]
[3,4,5]
また整列されたペア(2つの要素を持つタプル)に対する便利な関数が2つあります。
- Prelude>
fst (1, 2)
1
- Prelude>
snd (1, 2)
2
- Prelude>
map fst [(1, 2), (3, 4), (5, 6)]
[1,3,5]
how to work on listsも参照してください。
関数定義
main
と呼ばれるIO actionの定義について先に触れました。
main = do putStrLn "What is 2 + 2?"
x <- readLn
if x == 4
then putStrLn "You're right!"
else putStrLn "You're wrong!"
では次に、実際にfactorial
という関数の 関数" 定義を書いてみることで、上の例を補ってみましょう。丁寧に書こうと思うので、モジュールヘッダも追加します。
module Main where
factorial n = if n == 0 then 1 else n * factorial (n - 1)
main = do putStrLn "What is 5! ?"
x <- readLn
if x == factorial 5
then putStrLn "You're right!"
else putStrLn "You're wrong!"
ghc --make Test.hsと入力し再ビルドします。そして実行します。
$ ./Test What is 5! ? 120 You're right!
階数が得られました。ビルトイン関数のように、factorial 5
と括弧なしで関数が呼び出せました。
今度はghciを使って型を調べてみましょう。
$ ghci Test.hs << GHCi banner >> Ok, modules loaded: Main. Prelude Main> :tfactorial
factorial :: (Num a) => a -> a
関数の型は引数の型とともに表され、その後に->
が来て結果の型が書かれます。(結果も型クラスNum
となります)
階数は場合分けを使うことで簡単にできます。
factorial 0 = 1
factorial n = n * factorial (n - 1)
便利な構文
さらにいくつかのsyntaxが便利です。
secsToWeeks secs = let perMinute = 60
perHour = 60 * perMinute
perDay = 24 * perHour
perWeek = 7 * perDay
in secs / perWeek
let
式は一時的な名前を定義します。(ここでもレイアウトが使われています。好みによりますが、{ 中括弧 }とセミコロンによる区切りを使うこともできます)
classify age = case age of 0 -> "newborn"
1 -> "infant"
2 -> "toddler"
_ -> "senior citizen"
case
式は複数の場合分けを行います。特別なラベル _
は"その他すべて"を表します。
ライブラリを使う
ここまでのチュートリアルではPreludeの一部しか使ってきませんでした。PreludeはHaskellのどんなプログラムでも使うことができる関数のセットです。
ここから生産性の高いHaskellプログラマになるためには、あなたが必要とするPrelude以外の librariesについて明るくなるのがよいです。標準ライブラリに関してはhttp://haskell.org/ghc/docs/latest/html/libraries/にあります。モジュールは以下のものがあります。
- Useful data structures
- Concurrent and parallel programming
- Graphics and GUI libraries
- Networking, POSIX, and other system-level stuff
- Two test frameworks, QuickCheck and HUnit
- Regular expressions and predictive parsers
- More...
module Main where
import qualified Data.Map as M
errorsPerLine = M.fromList
[ ("Chris", 472), ("Don", 100), ("Simon", -5) ]
main = do putStrLn "Who are you?"
name <- getLine
case M.lookup name errorsPerLine of
Nothing -> putStrLn "I don't know you"
Just n -> do putStr "Errors per line: "
print n
import
はData.Map
にあるコードを使うことを宣言していて、それらはM
という別名を使うことが定義されています。(ここで行っていることは必要なことです。なぜならいくつかの関数はPrelude内の関数と同じ名前を持っているからです。たいていのライブラリではas
の部分は必要ありません。)
もし標準ライブラリにないものを必要とする場合は、 http://hackage.haskell.org/packages/hackage.html かこのWikiのapplications and librariesのページを見てください。これらは多くの方々がHaskellのために書いたたくさんのライブラリを集めたページです。ライブラリを手元に落としてきたら、展開してそのディレクトリに入って以下を実行してください:
runhaskell Setup configure runhaskell Setup build runhaskell Setup install
UNIXでは最後のrunhaskell Setup installを実行するためにrootになる必要があります。
10分以上かかるので省いたトピック
- Advanced data types
- Advanced syntax
- 発展的な関数
- Monads
- File I/O
- ファイルの読み込み
- ファイルの書き込み