Personal tools

10分で学ぶHaskell

From HaskellWiki

Jump to: navigation, search

Contents

1 概要

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コードを書いてすぐに結果を得ることができる環境です。

2 簡単な数式

たいていの数式は直接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

3 コンソール

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文字であれば動作しますが、おすすめできません。また中には使う人もいるかもしれませんが、プロポーショナルフォントを使うこともおすすめしません。

4 簡単な型

これまで、宣言に関して全く触れてきませんでした。それは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
    はあらゆる数値型として使うことができます。(このおかげで
    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アクションから
()
を返すことができます。

5 構造化されたデータ

基本データ型は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も参照してください。

6 関数定義

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)

7 便利な構文

さらにいくつかの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
式は複数の場合分けを行います。特別なラベル
_
は"その他すべて"を表します。

8 ライブラリを使う

ここまでのチュートリアルでは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
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になる必要があります。

9 10分以上かかるので省いたトピック

Languages: en zh/cn ja