Difference between revisions of "10分で学ぶHaskell"

From HaskellWiki
Jump to navigation Jump to search
(→‎構造化されたデータ: 訳の修正とコードの整形)
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
== 概要 ==
 
== 概要 ==
   
Haskellは関数型で(つまりすべてが関数呼びだしで処理される)、静的な暗黙的型付けで([[type]]はコンパイラによって確認され、明示的に宣言する必要はない)、遅延評価(必要となるまで処理されない)の言語です。系統が近い言語として最も人気のあるのはおそらくML系の言語でしょう。(MLは遅延評価ではないですが)
+
Haskellは関数型で(つまりすべてが関数呼びだしで処理される)、静的な暗黙的型付けで([[]]はコンパイラによって確認され、明示的に宣言する必要はない)、遅延評価(必要となるまで処理されない)の言語です。系統が近い言語として最も人気のあるのはおそらくML系の言語でしょう。(MLは遅延評価ではないですが)
   
 
最も普及している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>ghci</tt>に入力して計算結果を得ることができます。<tt>Prelude></tt>はGHCiのデフォルトのプロンプトです。
   
  +
<tt>
Prelude> <hask>3 * 5</hask>
 
  +
:Prelude> <hask>3 * 5</hask>
15
 
  +
:15
Prelude> <hask>4 ^ 2 - 1</hask>
 
  +
:Prelude> <hask>4 ^ 2 - 1</hask>
15
 
  +
:15
Prelude> <hask>(1 - 5)^(3 * 2 - 4)</hask>
 
  +
:Prelude> <hask>(1 - 5)^(3 * 2 - 4)</hask>
16
 
  +
:16
  +
</tt>
   
文字列は"ダブルクォート(二重引用符)"で囲みます。文字列の結合をするときは<hask>++</hask>を使います。
+
文字列はダブルクォート(二重引用符)で囲みます。文字列の結合をするときは<hask>++</hask>を使います。
   
  +
<tt>
Prelude> <hask>"Hello"</hask>
 
"Hello"
+
:Prelude> <hask>"Hello"</hask>
  +
:"Hello"
Prelude> <hask>"Hello" ++ ", Haskell"</hask>
 
"Hello, Haskell"
+
:Prelude> <hask>"Hello" ++ ", Haskell"</hask>
  +
:"Hello, Haskell"
  +
</tt>
   
[[function]]を呼び出すときは関数の後に直接引数を並べて行います。関数呼び出しに括弧は必要ありません。こんな感じです:
+
[[関数]]を呼び出すときは関数の後に直接引数を並べて行います。関数呼び出しに括弧は必要ありません。こんな感じです:
   
  +
<tt>
Prelude> <hask>succ 5</hask>
 
  +
:Prelude> <hask>succ 5</hask>
6
 
  +
:6
Prelude> <hask>truncate 6.59</hask>
 
  +
:Prelude> <hask>truncate 6.59</hask>
6
 
  +
:6
Prelude> <hask>round 6.59</hask>
 
  +
:Prelude> <hask>round 6.59</hask>
7
 
  +
:7
Prelude> <hask>sqrt 2</hask>
 
  +
:Prelude> <hask>sqrt 2</hask>
1.4142135623730951
 
  +
:1.4142135623730951
Prelude> <hask>not (5 < 3)</hask>
 
  +
:Prelude> <hask>not (5 < 3)</hask>
True
 
  +
:True
Prelude> <hask>gcd 21 14</hask>
 
  +
:Prelude> <hask>gcd 21 14</hask>
7
 
  +
:7
  +
</tt>
   
 
== コンソール ==
 
== コンソール ==
   
[[Introduction to IO |I/O actions]]はコンソールからの入出力を行うのに使います。有名なものの例は:
+
[[IO入門編|I/Oアクション]]はコンソールからの入出力を行うのに使います。よくある例は:
   
  +
<tt>
Prelude> <hask>putStrLn "Hello, Haskell"</hask>
 
Hello, Haskell
+
:Prelude> <hask>putStrLn "Hello, Haskell"</hask>
  +
:Hello, Haskell
Prelude> <hask>putStr "No newline"</hask>
 
No newlinePrelude> <hask>print (5 + 4)</hask>
+
:Prelude> <hask>putStr "No newline"</hask>
  +
:No newlinePrelude> <hask>print (5 + 4)</hask>
9
 
  +
:9
Prelude> <hask>print (1 < 2)</hask>
 
  +
:Prelude> <hask>print (1 < 2)</hask>
True
 
  +
: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>ブロックを使います。アクションはセミコロンで区切られます。
+
ひとつのの中で複数の I/O アクションを使いたい場合は、<hask>do</hask>ブロックを使います。アクションはセミコロンで区切られます。
   
  +
<tt>
Prelude> <hask>do { putStr "2 + 2 = " ; print (2 + 2) }</hask>
 
  +
:Prelude> <hask>do { putStr "2 + 2 = " ; print (2 + 2) }</hask>
2 + 2 = 4
 
  +
:2 + 2 = 4
Prelude> <hask>do { putStrLn "ABCDE" ; putStrLn "12345" }</hask>
 
  +
:Prelude> <hask>do { putStrLn "ABCDE" ; putStrLn "12345" }</hask>
ABCDE
 
  +
:ABCDE
12345
 
  +
:12345
  +
</tt>
   
読み込みは <hask>getLine</hask>(<hask>String</hask>を返します)か<hask>readLn</hask>(どんな型の値でも返します)でできます。<hask> <- </hask>シンボルはI/Oアクションの結果に名前を束縛するために使います。
+
読み込みは <hask>getLine</hask>(<hask>String</hask>を返します)か<hask>readLn</hask>(どんな型の値でも返します)でできます。<hask> <- </hask>という記号はI/Oアクションの結果に名前を付けるために使います。
   
  +
<tt>
Prelude> <hask>do { n <- readLn ; print (n^2) }</hask>
 
  +
:Prelude> <hask>do { n <- readLn ; print (n^2) }</hask>
4
 
  +
:4
16
 
  +
:16
  +
</tt>
   
 
(4が入力で16が結果です。)
 
(4が入力で16が結果です。)
   
実際には<hask>do</hask>ブロックを書くには別の方法があります。Haskellでは中括弧やセミコロンをあまり使わないので、インデントが重要になります。<tt>ghci</tt>では書きにくいかもしれないので、ソースファイル(例えば<tt>Test.hs</tt>)に書いてビルドしてみましょう。
+
別の書き方で<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>ブロックはそこで終わります。これは“レイアウト”と呼ばれ、Haskellでは、いつも行の区切り文字や中括弧を使うのは面倒なので、代わりにレイアウトを使います。(<hask>then</hask>句と<hask>else</hask>句はこれが理由で<hask>if</hask>よりインデントが深くなければならないのです。もし<hask>if</hask>と同じ列から開始した場合、<hask>if</hask>と別の行と解釈されてしまって、エラーとなります。)
The first non-space character after <hask>do</hask> is special. In this case, it's the <tt>p</tt> from <hask>putStrLn</hask>. Every line that starts in the same column as that <hask>p</hask> is another statement in the <hask>do</hask> block. If you indent more, it's part of the previous statement. If you indent less, it ends the <hask>do</hask> block. This is called "layout", and Haskell uses it to avoid making you put in statement terminators and braces all the time. (The <hask>then</hask> and <hask>else</hask> phrases have to be indented for this reason: if they started in the same column, they'd be separate statements, which is wrong.)
 
   
  +
(Note: レイアウトを使う場合はインデントにタブを'''用いないでください'''。技術的にはタブが8文字であれば動作しますが、おすすめできません。また中には使う人もいるかもしれませんが、プロポーショナルフォントを使うこともおすすめしません。
(Note: Do '''not''' indent with tabs if you're using layout. It technically still works if your tabs are 8 spaces, but it's a bad idea. Also, don't use proportional fonts -- which apparently some people do, even when programming!)
 
   
== 簡な型 ==
+
== 簡な型 ==
   
  +
これまで、[[型]]宣言に関して全く触れてきませんでした。それはHaskellが型推論を行うからです。一般的に型宣言はしたい場合以外はする必要がありません。型宣言をする場合は<hask>::</hask>を使って行います。
So far, not a single [[type]] declaration has been mentioned. That's because Haskell does type inference. You generally don't have to declare types unless you want to. If you do want to declare types, you use <hask>::</hask> to do it.
 
   
  +
<tt>
Prelude> <hask>5 :: Int</hask>
 
  +
:Prelude> <hask>5 :: Int</hask>
5
 
  +
:5
Prelude> <hask>5 :: Double</hask>
 
  +
:Prelude> <hask>5 :: Double</hask>
5.0
 
  +
:5.0
  +
</tt>
   
  +
Haskellでは[[型]] (そして後で触れる型[[クラス]])は常に大文字から開始します。変数は常に小文字から始まります。これは言語仕様であり、命名規則([[Studly capitals|naming convention]])ではありません。
[[Type]]s (and type [[class]]es, discussed later) always start with upper-case letters in Haskell. Variables always start with lower-case letters. This is a rule of the language, not a [[Studly capitals|naming convention]].
 
   
  +
<tt>ghci</tt>を使って、ある値がどんな型かを確認することもできます。普段、自分では型宣言をしないので、この機能は役に立ちます。
You can also ask <tt>ghci</tt> what type it has chosen for something. This is useful because you don't generally have to declare your types.
 
   
 
Prelude> :t <hask>True</hask>
 
Prelude> :t <hask>True</hask>
Line 104: Line 118:
 
<hask>"Hello, Haskell" :: [Char]</hask>
 
<hask>"Hello, Haskell" :: [Char]</hask>
   
(In case you noticed, <hask>[Char]</hask> is another way of saying <hask>String</hask>. See the [[#Structured data|section on lists]] later.)
+
(上の例を見て気づいたように、<hask>[Char]</hask><hask>String</hask>の別名です。あとでを[[#Structured data|section on lists]]を見てみましょう)
   
  +
数字に関してはもっと面白いものが見られます。
Things get more interesting for numbers.
 
   
  +
<tt>
Prelude> :t <hask>42</hask>
 
<hask>42 :: (Num t) => t</hask>
+
:Prelude> :t <hask>42</hask>
Prelude> :t <hask>42.0</hask>
+
:<hask>42 :: (Num t) => t</hask>
<hask>42.0 :: (Fractional t) => t</hask>
+
:Prelude> :t <hask>42.0</hask>
Prelude> :t <hask>gcd 15 20</hask>
+
:<hask>42.0 :: (Fractional t) => t</hask>
<hask>gcd 15 20 :: (Integral t) => t</hask>
+
:Prelude> :t <hask>gcd 15 20</hask>
  +
:<hask>gcd 15 20 :: (Integral t) => t</hask>
  +
</tt>
   
  +
これらの型は“型クラス”を用いています。つまり:
These types use "type classes." They mean:
 
   
* <hask>42</hask> can be used as any numeric type. (This is why I was able to declare <hask>5</hask> as either an <hask>Int</hask> or a <hask>Double</hask> earlier.)
+
* <hask>42</hask> はあらゆる数値型として使うことができます。(このおかげで<hask>5</hask><hask>Int</hask>としても<hask>Double</hask>としても宣言できたわけです)
  +
* <hask>42.0</hask> はどのような小数型にもなれますが、整数型にはなれません。
* <hask>42.0</hask> can be any fractional type, but not an integral type.
 
  +
* <hask>gcd 15 20</hask> (ちなみにこれは関数呼び出しです)はどのような整数型にもなれますが、小数型にはなれません。
* <hask>gcd 15 20</hask> (which is a function call, incidentally) can be any integral type, but not a fractional type.
 
   
  +
Haskellの“Prelude”(標準ライブラリのうち初期状態でimportされているモジュール)内には5種類の数値型があります。
There are five numeric types in the Haskell "prelude" (the part of the library you get without having to import anything):
 
   
* <hask>Int</hask> is an integer with at least 30 bits of precision.
+
* <hask>Int</hask> は少なくとも30ビットの精度の整数です
* <hask>Integer</hask> is an integer with unlimited precision.
+
* <hask>Integer</hask> は精度無制限の整数型です
* <hask>Float</hask> is a single precision floating point number.
+
* <hask>Float</hask> は単精度浮動小数点数です
* <hask>Double</hask> is a double precision floating point number.
+
* <hask>Double</hask> は倍精度浮動小数点数です
* <hask>Rational</hask> is a fraction type, with no rounding error.
+
* <hask>Rational</hask> は分数型で丸め誤差がありません
   
All five are '''instances''' of the <hask>Num</hask> type class. The first two are '''instances''' of <hask>Integral</hask>, and the last three are '''instances''' of <hask>Fractional</hask>.
+
これら5つはすべて <hask>Num</hask>型クラスの '''インスタンス''' です。最初の2つは<hask>Integral</hask> '''インスタンス''' で、残り3つは<hask>Fractional</hask>の '''インスタンス''' です。
   
  +
ごちゃまぜにしてみましょう。
Putting it all together,
 
   
  +
<tt>
Prelude> <hask>gcd 42 35 :: Int</hask>
 
  +
:Prelude> <hask>gcd 42 35 :: Int</hask>
7
 
  +
:7
Prelude> <hask>gcd 42 35 :: Double</hask>
 
  +
:Prelude> <hask>gcd 42 35 :: Double</hask>
 
  +
:&nbsp;
<interactive>:1:0:
 
  +
:<interactive>:1:0:
No instance for (Integral Double)
 
  +
:&nbsp;&nbsp;&nbsp;&nbsp;No instance for (Integral Double)
  +
</tt>
   
  +
最後に触れておいたほうがよい型は <hask>()</hask>で表されるもので "Unit" と呼ばれます。この型はたった一つの値をとり、やはり<hask>()</hask>と書かれ "Unit" と呼ばれます。
The final type worth mentioning here is <hask>()</hask>, pronounced "unit." It only has one value, also written as <hask>()</hask> and pronounced "unit."
 
   
  +
<tt>
Prelude> <hask>()</hask>
 
<hask>()</hask>
+
:Prelude> <hask>()</hask>
Prelude> :t <hask>()</hask>
+
:<hask>()</hask>
<hask>() :: ()</hask>
+
:Prelude> :t <hask>()</hask>
  +
:<hask>() :: ()</hask>
  +
</tt>
   
  +
これはC言語系の言語での<tt>void</tt>キーワードに似ています。もしなにも返り値を期待しない場合、I/Oアクションから<hask>()</hask>を返すことができます。
You can think of this as similar to the <tt>void</tt> keyword in C family languages. You can return <hask>()</hask> from an I/O action if you don't want to return anything.
 
   
 
== 構造化されたデータ ==
 
== 構造化されたデータ ==
   
  +
基本データ型は2つの方法で容易にまとめることができます。一つはリストで角括弧 [ ] で囲います。もう一つはタプルで丸括弧 ( ) で囲みます。
Basic data types can be easily combined in two ways: lists, which go in [square brackets], and tuples, which go in (parentheses).
 
   
  +
リストは同じ型の複数の値を保持する場合に使われます。
Lists are used to hold multiple values of the same type.
 
   
  +
<tt>
Prelude> <hask>[1, 2, 3]</hask>
 
[1,2,3]
+
:Prelude> <hask>[1, 2, 3]</hask>
  +
:[1,2,3]
Prelude> <hask>[1 .. 5]</hask>
 
  +
:Prelude> <hask>[1 .. 5]</hask>
[1,2,3,4,5]
 
  +
:[1,2,3,4,5]
Prelude> <hask>[1, 3 .. 10]</hask>
 
  +
:Prelude> <hask>[1, 3 .. 10]</hask>
[1,3,5,7,9]
 
  +
:[1,3,5,7,9]
Prelude> <hask>[True, False, True]</hask>
 
[True,False,True]
+
:Prelude> <hask>[True, False, True]</hask>
  +
:[True,False,True]
  +
</tt>
   
  +
文字列は単に文字のリストにすぎません。
Strings are just lists of characters.
 
   
  +
<tt>
Prelude> <hask>['H', 'e', 'l', 'l', 'o']</hask>
 
  +
:Prelude> <hask>['H', 'e', 'l', 'l', 'o']</hask>
"Hello"
 
  +
:"Hello"
  +
</tt>
   
  +
<hask>:</hask>演算子は要素をリストの先頭に追加します。(Lisp系言語でいうところの<tt>cons</tt>関数です)
The <hask>:</hask> operator appends an item to the beginning of a list. (It is Haskell's version of the <tt>cons</tt> function in the Lisp family of languages.)
 
   
  +
<tt>
Prelude> <hask>'C' : ['H', 'e', 'l', 'l', 'o']</hask>
 
  +
:Prelude> <hask>'C' : ['H', 'e', 'l', 'l', 'o']</hask>
"CHello"
 
  +
:"CHello"
  +
</tt>
   
  +
タプルは決まった数の値を保持できます。ただしそれぞれの型は異なっていても構いません。
Tuples hold a fixed number of values, which can have different types.
 
   
  +
<tt>
Prelude> <hask>(1, True)</hask>
 
(1,True)
+
:Prelude> <hask>(1, True)</hask>
  +
:(1,True)
Prelude> <hask>zip [1 .. 5] ['a' .. 'e']</hask>
 
  +
:Prelude> <hask>zip [1 .. 5] ['a' .. 'e']</hask>
[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')]
 
  +
:[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')]
  +
</tt>
   
  +
この例の最後で <hask>zip</hask>関数 が使われています。これは2つのリストを1つのタプルのリストにするライブラリ関数です。
The last example used <hask>zip</hask>, a library function that turns two lists into a list of tuples.
 
   
  +
型は予想どおり以下のようになります。
The types are probably what you'd expect.
 
   
  +
<tt>
Prelude> :t <hask>['a' .. 'c']</hask>
 
<hask>['a' .. 'c'] :: [Char]</hask>
+
:Prelude> :t <hask>['a' .. 'c']</hask>
Prelude> :t <hask>[('x', True), ('y', False)]</hask>
+
:<hask>['a' .. 'c'] :: [Char]</hask>
<hask>[('x', True), ('y', False)] :: [(Char, Bool)]</hask>
+
:Prelude> :t <hask>[('x', True), ('y', False)]</hask>
  +
:<hask>[('x', True), ('y', False)] :: [(Char, Bool)]</hask>
  +
</tt>
   
  +
リストはHaskellではとてもよく使われます。リストを操作する便利な関数がいくつもあります。
Lists are used a lot in Haskell. There are several functions that do nice things with them.
 
   
  +
<tt>
Prelude> <hask>[1 .. 5]</hask>
 
<hask>[1,2,3,4,5]</hask>
+
:Prelude> <hask>[1 .. 5]</hask>
Prelude> <hask>map (+ 2) [1 .. 5]</hask>
+
:<hask>[1,2,3,4,5]</hask>
<hask>[3,4,5,6,7]</hask>
+
:Prelude> <hask>map (+ 2) [1 .. 5]</hask>
Prelude> <hask>filter (> 2) [1 .. 5]</hask>
+
:<hask>[3,4,5,6,7]</hask>
<hask>[3,4,5]</hask>
+
:Prelude> <hask>filter (> 2) [1 .. 5]</hask>
  +
:<hask>[3,4,5]</hask>
  +
</tt>
   
  +
また整列されたペア(2つの要素を持つタプル)に対する便利な関数が2つあります。
There are two nice functions on ordered pairs (tuples of two elements):
 
   
  +
<tt>
Prelude> <hask>fst (1, 2)</hask>
 
<hask>1</hask>
+
:Prelude> <hask>fst (1, 2)</hask>
Prelude> <hask>snd (1, 2)</hask>
+
:<hask>1</hask>
<hask>2</hask>
+
:Prelude> <hask>snd (1, 2)</hask>
Prelude> <hask>map fst [(1, 2), (3, 4), (5, 6)]</hask>
+
:<hask>2</hask>
<hask>[1,3,5]</hask>
+
:Prelude> <hask>map fst [(1, 2), (3, 4), (5, 6)]</hask>
  +
:<hask>[1,3,5]</hask>
  +
</tt>
   
Also see [[how to work on lists]]
+
[[how to work on lists]]も参照してください。
   
== [[Function]] 定義 ==
+
== [[関数]]定義 ==
   
We wrote a definition of an [[Introduction to Haskell IO/Actions |IO action]] earlier, called <hask>main</hask>:
+
<hask>main</hask>と呼ばれる[[Introduction to Haskell IO/Actions |IO action]]の定義について先に触れました。
   
 
<haskell>
 
<haskell>
Line 222: Line 256:
 
</haskell>
 
</haskell>
   
  +
では次に、実際に<hask>factorial</hask>という関数の ''[[関数]]" 定義を書いてみることで、上の例を補ってみましょう。丁寧に書こうと思うので、モジュールヘッダも追加します。
Now, let's supplement it by actually writing a ''[[function]]'' definition and call it <hask>factorial</hask>. I'm also adding a module header, which is good form.
 
   
 
<haskell>
 
<haskell>
Line 236: Line 270:
 
</haskell>
 
</haskell>
   
Build again with <tt>ghc --make Test.hs</tt>. And,
+
<tt>ghc --make Test.hs</tt>と入力し再ビルドします。そして実行します。
   
 
$ ./Test
 
$ ./Test
Line 243: Line 277:
 
You're right!
 
You're right!
   
  +
階数が得られました。ビルトイン関数のように、<hask>factorial 5</hask>と括弧なしで関数が呼び出せました。
There's a function. Just like the built-in functions, it can be called as <hask>factorial 5</hask> without needing parentheses.
 
   
Now ask <tt>ghci</tt> for the [[type]].
+
今度は<tt>ghci</tt>を使って[[]]を調べてみましょう。
   
 
$ ghci Test.hs
 
$ ghci Test.hs
Line 253: Line 287:
 
<hask>factorial :: (Num a) => a -> a</hask>
 
<hask>factorial :: (Num a) => a -> a</hask>
   
  +
関数の型は引数の型とともに表され、その後に<hask> -> </hask>が来て結果の型が書かれます。(結果も型クラス<hask>Num</hask>となります)
Function types are written with the argument type, then <hask> -> </hask>, then the result type. (This also has the type class <hask>Num</hask>.)
 
   
  +
階数は場合分けを使うことで簡単にできます。
Factorial can be simplified by writing it with case analysis.
 
   
 
<haskell>
 
<haskell>
Line 264: Line 298:
 
== 便利な構文 ==
 
== 便利な構文 ==
   
A couple extra pieces of [[:Category:Syntax |syntax]] are helpful.
+
さらにいくつかの[[:Category:Syntax |syntax]]が便利です。
   
 
<haskell>
 
<haskell>
Line 274: Line 308:
 
</haskell>
 
</haskell>
   
  +
<hask>let</hask>式は一時的な名前を定義します。(ここでもレイアウトが使われています。好みによりますが、{ 中括弧 }とセミコロンによる区切りを使うこともできます)
The <hask>let</hask> expression defines temporary names. (This is using layout again. You could use {braces}, and separate the names with semicolons, if you prefer.)
 
   
 
<haskell>
 
<haskell>
Line 283: Line 317:
 
</haskell>
 
</haskell>
   
The <hask>case</hask> expression does a multi-way branch. The special label <hask>_</hask> means "anything else".
+
<hask>case</hask>式は複数の場合分けを行います。特別なラベル <hask>_</hask> "その他すべて"を表します。
   
 
== ライブラリを使う ==
 
== ライブラリを使う ==
   
  +
ここまでのチュートリアルでは[[Prelude]]の一部しか使ってきませんでした。PreludeはHaskellのどんなプログラムでも使うことができる関数のセットです。
Everything used so far in this tutorial is part of the [[Prelude]], which is the set of Haskell functions that are always there in any program.
 
   
The best road from here to becoming a very productive Haskell programmer (aside from practice!) is becoming familiar with other [[Applications and libraries | libraries]] that do the things you need. Documentation on the standard libraries is at [http://haskell.org/ghc/docs/latest/html/libraries/ http://haskell.org/ghc/docs/latest/html/libraries/]. There are modules there with:
+
ここから生産性の高いHaskellプログラマになるためには、あなたが必要とするPrelude以外の[[Applications and libraries | libraries]]について明るくなるのがよいです。標準ライブラリに関しては[http://haskell.org/ghc/docs/latest/html/libraries/ http://haskell.org/ghc/docs/latest/html/libraries/]にあります。モジュールは以下のものがあります。
   
 
* [[Applications and libraries/Data structures |Useful data structures]]
 
* [[Applications and libraries/Data structures |Useful data structures]]
Line 315: Line 349:
 
</haskell>
 
</haskell>
   
  +
<hask>import</hask>は<hask>Data.Map</hask>にあるコードを使うことを宣言していて、それらは<hask>M</hask>という別名を使うことが定義されています。(ここで行っていることは必要なことです。なぜならいくつかの関数はPrelude内の関数と同じ名前を持っているからです。たいていのライブラリでは<hask>as</hask>の部分は必要ありません。)
The <hask>import</hask> says to use code from <hask>Data.Map</hask> and that it will be prefixed by <hask>M</hask>. (That's necessary because some of the functions have the same names as functions from the prelude. Most libraries don't need the <hask>as</hask> part.)
 
   
  +
もし標準ライブラリにないものを必要とする場合は、 http://hackage.haskell.org/packages/hackage.html かこのWikiの[[applications and libraries]]のページを見てください。これらは多くの方々がHaskellのために書いたたくさんのライブラリを集めたページです。ライブラリを手元に落としてきたら、展開してそのディレクトリに入って以下を実行してください:
If you want something that's not in the standard library, try looking at http://hackage.haskell.org/packages/hackage.html or this wiki's [[applications and libraries]] page. This is a collection of many different libraries written by a lot of people for Haskell. Once you've got a library, extract it and switch into that directory and do this:
 
   
 
runhaskell Setup configure
 
runhaskell Setup configure
Line 323: Line 357:
 
runhaskell Setup install
 
runhaskell Setup install
   
  +
UNIXでは最後の<tt>runhaskell Setup install</tt>を実行するためにrootになる必要があります。
On a UNIX system, you may need to be root for that last part.
 
   
 
== 10分以上かかるので省いたトピック ==
 
== 10分以上かかるので省いたトピック ==
   
 
* [[:Category:Language | Advanced data types]]
 
* [[:Category:Language | Advanced data types]]
  +
** 数学的なリスト
** Arithmetic lists
 
 
** [[List comprehension]]s
 
** [[List comprehension]]s
 
** [[Type#Type and newtype | Type synonyms]]
 
** [[Type#Type and newtype | Type synonyms]]
Line 337: Line 371:
 
** [[Infix operator |(+) and `foo`]]
 
** [[Infix operator |(+) and `foo`]]
 
** [[Fixity declaration]]s
 
** [[Fixity declaration]]s
  +
* 発展的な関数
* Advanced functions
 
 
** [[Currying]]
 
** [[Currying]]
 
** [[Lambda abstraction]]s
 
** [[Lambda abstraction]]s
Line 343: Line 377:
 
* [[:Category:Monad |Monads]]
 
* [[:Category:Monad |Monads]]
 
* [[Tutorials/Programming Haskell/String IO |File I/O]]
 
* [[Tutorials/Programming Haskell/String IO |File I/O]]
  +
** ファイルの読み込み
** Reading files
 
  +
** ファイルの書き込み
** Writing Files
 
   
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]

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 MacOSWindowsSolarisで動作します。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

読み込みは getLineStringを返します)か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> :t True
 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 はあらゆる数値型として使うことができます。(このおかげで5Intとしても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> :t factorial
 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/にあります。モジュールは以下のものがあります。

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

importData.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分以上かかるので省いたトピック

Languages: en zh/cn ja