プログラミング言語/Haskell
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
*Haskell [#d5adcc0a]
#contents
*能書き [#ycc6155f]
**Haskellの特徴 [#nf0358a2]
Haskellのおもな特徴を列挙すると次のようになる。この手の言...
-代数的データ型と、パターン照合。
-インデントを利用した簡潔な構文。
-型推論つき静的型付け。
-型推論と多重定義を両立する、型クラス。
-純粋な(つまり、式に副作用がない)関数型であること。
-第一級の動作による入出力。
**関数型言語って何 [#l5605168]
Haskellは純粋関数型言語に分類される。関数型言語というのは...
現在主流の言語は、ほぼ全て手続き型言語だ。手続き的プログ...
1からnまでの整数の中で、2でも3でも割り切れないものの総和...
整数を6で割った余りに着目する賢い方法もあるが、ここでは素...
「総和」を0とする。
「現在の整数」が0からnまで動く間、以下の一行を繰り返す:
もし、「現在の整数」が2でも3でも割り切れないなら、「総...
この時点で「総和」が問題の答えになっている。
このプログラムには、「「総和」を0とする」とか、「「総和」...
一方、関数的プログラミングでは、データに着目する。何か問...
1からnまでの整数の列をAとする。
Aから、2でも3でも割り切れない整数のみを取り出した列をBと...
Bの要素を全て足し合わせたものが問題の答えである。
このプログラムには命令はどこにも出てこない。ただ入力と出...
このふたつのスタイルの違いは、言語の違いにそのまま対応す...
手続き型言語は、現実のコンピュータと相性の良い構造になっ...
**おすすめ [#x0716709]
上のような特徴から、少なくとも以下のような人にはHaskellを...
-手続き型言語に飽きた。
-命令するのよりも記述する方が好き。
-複雑な記号操作を必要とするプログラム(言語処理系とか)を書...
-俗世よりも抽象的・記号的な世界に興味を惹かれる。数学好き...
*処理系を導入する [#bc9decb0]
**入れる処理系を選ぶ [#p188b359]
2011年6月の時点では、以下の処理系・パッケージがメンテナン...
:[[Haskell Platform:http://hackage.haskell.org/platform/]...
:[[GHC:http://www.haskell.org/ghc/]]|対話環境とインタプリ...
:[[Hugs:http://www.haskell.org/hugs/]]|対話環境とインタプ...
ここでは、利用者の多いと思われるGHCのインストールを説明す...
**GHCのインストール [#n589bef9]
***Windows [#hc33c8f1]
[[GHCのダウンロードページ:http://www.haskell.org/ghc/down...
コマンドプロンプトを開き、「ghc --version」と入力してみる。
The Glorious Glasgow Haskell Compilation System, version...
こんな感じのものが表示されれば成功。
***UNIX互換OS [#e399dfbb]
OS、ディストリビューションごとにパッケージが用意されてい...
***その他のプラットフォーム [#f94399f7]
[[GHCのダウンロードページ:http://www.haskell.org/ghc/down...
*チュートリアル [#sbc437ae]
**式 [#n212859c]
プログラムは計算をしたり、それ以外のことをしたりするが、H...
***対話環境 [#s3caf7cf]
実際にHaskellで計算をしてみよう。まずGHC付属の対話環境、G...
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for...
Loading package base ... linking ... done.
Prelude>
最後の行は「Prelude>」となっていて、行末にカーソルがあり...
Prelude> 33 + 34
67
Prelude>
こちらの「33 + 34」という入力に対して、GHCiは「67」という...
さて、この場合、GHCiは「33 + 34」を計算して結果「67」を得...
GHCiを終了させたいときは「:quit」または短縮して「:q」と入...
***演算子と関数 [#a137e737]
Haskellでは足し算以外にも多くの種類の式を扱うことが出来る...
まず、足し算と同じように引き算も使える。
Prelude> 33 - 34
-1
この二つは自由に組み合わせることができる。
Prelude> 3 + 6 - 1 - 4
4
スペースはあってもなくても良い。
Prelude> 3+6 - 1- 4
4
単独の整数は、それ自体で式になる。
Prelude> 7
7
掛け算は「*」で表す。
Prelude> 2 * 7
14
数学と同じように、掛け算は加減算よりも優先される。これに...
Prelude> 1 + 2 * 3
7
Prelude> (1 + 2) * 3
9
括弧を多重に使いたいときは、そのまま入れ子にすれば良い。
Prelude> (14 - (1 + 2)) * 4
44
「+」とか「-」とか「*」のような記号を''演算子''と呼ぶ。
除算は少し勝手が違う。整数の除算の結果は商と余りの二つで...
Prelude> div 7 3
2
Prelude> mod 7 3
1
divやmodは''関数''だ。Haskellにおいて関数とは、数学でいう...
Haskellで関数適用を表現する時は、「div 7 3」のように関数...
Prelude> div 7 3 + 1
3
Prelude> div 7 (3 + 1)
1
divやmodは引数を二つ取る関数だが、そうでない関数もある。...
Prelude> negate 4
-4
Prelude> negate (2 - 5)
3
入力の値から出力の値が決まる、という点では、演算子は関数...
Prelude> (+) 1 2
3
Prelude> (-) ((*) 3 4) 7
5
***エラー [#pf637a00]
入力する式を打ち間違えると、GHCiはエラーメッセージを出力...
式が入力されると、GHCiはまず式の構造を解析する。どのカッ...
Prelude> 4 +
<interactive>:1:3: parse error (possibly incorrect inden...
Prelude> (2 + 3)) * 4
<interactive>:1:7: parse error on input `)'
式が構文解析に成功して、GHCiが構造を把握すると、次に「型...
Prelude> div + mod
<interactive>:1:0:
No instance for (Num (a -> a -> a))
arising from use of `+' at <interactive>:1:0-8
Possible fix: add an instance declaration for (Num (...
In the expression: div + mod
In the definition of `it': it = div + mod
「No instance for...」というエラーメッセージが型エラーを...
構文的ミスによって型エラーが起きることもある。括弧をつけ...
Prelude> div div 34 3 2
<interactive>:1:0:
No instance for (Integral (a -> a -> a))
arising from use of `div' at <interactive>:1:0-13
Possible fix:
add an instance declaration for (Integral (a -> a ...
In the expression: div div 34 3 2
In the definition of `it': it = div div 34 3 2
この場合、関数「div」がdiv, 34, 3, 2という4つの引数を伴っ...
型検査に成功すれば、正しいHaskellの式だと確認されたことに...
Prelude> div 6 (3 - 3)
*** Exception: divide by zero
評価中に発生したエラーは「*** Exception: ...」と報告され...
なお、例えば「div」を単独で評価しようとすると以下のように...
Prelude> div
<interactive>:1:0:
No instance for (Show (a -> a -> a))
arising from a use of `print' at <interactive>:1:0-2
Possible fix: add an instance declaration for (Show ...
In the expression: print it
In a 'do' expression: print it
これは「div」が式として間違っているというのではなく、「di...
**定義 [#r22e0d79]
***ソースファイルと変数 [#e2982bf8]
なんらかの理由で、百万秒が何日間に相当するか知りたくなっ...
Prelude> 60 * 60 * 24 -- 一分が60秒で、一時間が60分で、...
86400
Prelude> div 1000000 86400 -- 一日が86400秒だから、百万...
11
これで、端数を切り捨てて十一日だということが分かった。な...
では、逆に、14日間は何秒かを知りたくなったとしよう。一日...
Prelude> 14 * 86400
1209600
もし、この手の計算を何度もしなければならないなら、その都...
値に名前を付けたもののことを''変数''という。変数を使うに...
Haskellソースファイルの中身はテキストファイルだが、拡張子...
day :: Int
day = 86400
これをGHCiから使うには、:loadコマンド(省略形は:l)でロード...
Prelude> :load day.hs
[1 of 1] Compiling Main ( day.hs, interprete...
Ok, modules loaded: Main.
*Main>
GHCiをコマンドラインから起動しているなら、起動時の引数と...
これで、式のなかで86400の代わりにdayという変数を使えるよ...
*Main> day
86400
*Main> day + 1
86401
*Main> div 8000000 day
92
これでいちいち長い数値をタイプする必要はないし、この数値...
なお、day.hsをロードした状態でこれを編集したら、:reloadコ...
さて、ソースファイルの中身を解説しよう。day.hsを再掲する。
day :: Int
day = 86400
二行目は、変数dayを、値86400を持つ変数として定義する、と...
今回、一日の秒数をGHCiで予め計算して、その値を使ってdayを...
day :: Int
day = 60 * 60 * 24
一つのソースファイルには、いくつでも変数の定義を書くこと...
-- 一分の秒数
minute :: Int
minute = 60
-- 一時間の秒数
hour :: Int
hour = minute * 60
-- 一日の秒数
day :: Int
day = hour * 24
このように、ソースファイル中には、コメントや空行を自由に...
複数の定義を書く場合、順番はどうでもいい。例えば、上のフ...
-- 一日の秒数
day :: Int
day = hour * 24
-- 一時間の秒数
hour :: Int
hour = minute * 60
-- 一分の秒数
minute :: Int
minute = 60
***関数 [#n0fdbe51]
ここまでに定義したdayとhourを使えば、例えば220万秒が何日...
*Main> div 2200000 day -- 220万秒は何日か?
25
*Main> mod 2200000 day -- 残りの秒数は?
40000
*Main> div 40000 hour -- 40000秒は何時間か?
11
よって、220万秒は25日と11時間だ。しかし、秒数が何日何時間...
このような機能は、関数を使うとうまく表現できる。前に説明...
この関数の結果は、例えば引数が100000ならdiv 100000 day、...
\secs -> div secs day
つまり、関数を作るには、まず、何か変数を適当に決めて(この...
ここでのsecsのような変数を''仮引数''という。仮引数の名前...
さて、こうして作った関数は、通常の関数と全く同じように適...
*Main> (\secs -> div secs day) 2200000
25
*Main> (\secs -> div secs day) 1200000
13
これで無事に関数を作ることができたので、あとは名前を付け...
secsToDays :: Int -> Int
secsToDays = \secs -> div secs day
二行目はいつもどおりの変数定義だ。一行目で「Int -> Int」...
これをGHCiにロードすれば、秒数から日数への変換が簡単にで...
*Main> secsToDays 1234567
14
*Main> secsToDays 6666666
77
:練習問題|与えられた秒数が何日何時間に相当するか、という...
***多引数関数 [#bf01cb50]
ついでに、引数を二つ以上取る関数の書き方も見ておこう。関...
\x y -> div (x + y) 2
この関数の型は「Int -> Int -> Int」になる。一般に、N引数...
**プログラム [#n31e6651]
これで準備が整ったので、実際に動作するプログラムを書く方...
***Hello, world [#q5fd2d0d]
次のソースコードをhello.hsとしよう。これは、標準出力に「H...
main :: IO ()
main = putStrLn "Hello, world"
コマンドラインで、hello.hsのあるディレクトリに移動し、次...
ghc --make hello.hs
すると、メッセージが表示され、同じディレクトリにhello(Win...
なお、Windowsで、エクスプローラからhello.exeを起動すると...
今、ghcコマンドで実行ファイルを生成してからそれを実行する...
$ runghc hello.hs
Hello, world
さて、hello.hsを解説しよう。再掲する。
main :: IO ()
main = putStrLn "Hello, world"
前節でみたとおり、これは「main」という変数を一つ定義する...
mainの型は「IO ()」と指定されている。これはmainの値が''IO...
Haskellプログラムが実行されるときは、変数mainの値がコマン...
このプログラムでは、mainの値は「putStrLn "Hello, world"」...
"Hello, world"というのは、そのまま「Hello, world」という...
putStrLnはコマンドを作る関数だ。引数が文字列xなら、結果は...
なお、putStrLnは「文字列を出力して改行せよ」というコマン...
***コマンドの合成 [#h47be662]
putStrLnを使えば、好きな文字列を標準出力に表示して改行す...
main :: IO ()
main = putStrLn "1st line" >> putStrLn "2nd line"
このプログラムを実行すると、「1st line」と表示して改行し...
合成されたコマンドもまたコマンドなので、新たな合成の材料...
このようなコマンドの合成は頻繁に使われるので、Haskellには...
main :: IO ()
main = do
putStrLn "1st line"
putStrLn "2nd line"
doという単語に続けて複数の式(ここでは二つ)を並べると、全...
式を並べるとき、以下の二つの規則に従う必要がある。
- 一行に一つの式を書くこと。ただし、途中に空行やコメント...
- 式の開始位置を揃えること。この例では、二つの式がどちら...
これさえ守れば、字下げの量は自由だ。例えば、半角スペース...
main :: IO ()
main = do
putStrLn "1st line"
putStrLn "2nd line"
doの直後で改行しないスタイルもある。
main :: IO ()
main = do putStrLn "1st line"
putStrLn "2nd line"
また、長い式を複数行に分けて書きたい場合、ある行を標準よ...
do
a
b c d
e f
g h i
j
do
a
b c d e f g h i
j
これらの規則を''配置規則''といって、Haskellではdo式の他に...
以上から分かるように、「do」という単語は文法上で特別の意...
case class data default deriving do else foreign if impo...
***コマンドの結果 [#wd034879]
プログラムが何か役に立つことをしようと思ったら、外部から...
getLineは標準入力から一行読むコマンドだ。このコマンドが実...
実際にgetLineを実行してみよう。
main :: IO String
main = getLine
このプログラムを実行すると、プログラムはすぐに入力待ちに...
今はgetLineの結果を捨てたが、これを利用することを考えよう...
>>=演算子は>>に似ていて、左辺を実行してから右辺を実行...
今、getLineの結果を使ってするべきことは、それを表示して改...
main :: IO ()
main = getLine >>= putStrLn
これを実際に実行して、入力した文字列がそのまま表示される...
具体的に何が起こるか見ておこう。「getLine >>= putStrLn」...
この種の合成も、do式を使って書ける。例えば、このプログラ...
main :: IO ()
main = do
line <- getLine
putStrLn line
一般に、do式中に式を並べるとき、式を書く代わりに「変数 <-...
ところで、上で「T型の結果を生成するコマンドの型はIO Tだ」...
***GHCiとコマンド [#h1dcd27b]
コマンドは、関数と同じく、表示できないデータだ。しかし、G...
Prelude> putStrLn "Helium"
Helium
これは、GHCiがコマンドを特別扱いするためだ。通常、GHCiは...
Prelude> getLine
infornography
"infornography"
これは便利な機能だが、慣れないうちは混乱の原因になること...
**文字列 [#jc1a35cf]
文字列は、外部とのやりとりをするのに重要なデータだ。標準...
***文字列リテラル [#n7cbaa1c]
前に紹介したように、文字列とは文字が0個以上並んだものだ。...
Haskellでいう文字とは、Unicodeコードポイントのことだ。厳...
このような目に見える文字の他に、特殊な効果を持つ文字群が...
既に使っているが、プログラム中で文字列の値を書き表すには...
文字列リテラル中に制御文字を直接書くことはできない。例え...
s :: String
s = "温情
判決"
これは、次のように書かないといけない。
s :: String
s = "温情\n判決"
このように、制御文字を含んだ文字列リテラルを書きたいとき...
Unicodeコードポイントを直接番号で指定することもできる。例...
Prelude> "用心棒"
"\29992\24515\26834"
なお、ソースファイル中のコメント以外の場所でASCIIの範囲外...
***文字列操作 [#rf7f4deb]
演算子++を使うと、文字列を連結できる。
Prelude> "rear" ++ "range"
"rearrange"
これを使うと、例えば、名前を尋ねて、その名前で挨拶するプ...
main :: IO ()
main = do
putStrLn "What's your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ ".")
関数lengthは、文字列の長さ(文字数)を返す。
Prelude> length "天上天下唯我独尊"
8
Prelude> length "a\nb"
3
***整数と文字列の相互変換 [#g085d877]
show関数を使うと、整数を10進表記の文字列に変換できる。
Prelude> show (7 * 8)
"56"
これを使うと、整数を使って計算し、その計算結果を出力する...
day :: Int
day = 60 * 60 * 24
main :: IO ()
main = putStrLn ("1 day = " ++ show day ++ " seconds.")
read関数は逆に、10進表記された文字列を整数に変換する。例...
main :: IO ()
main = do
putStrLn "Input an integer."
input <- getLine
putStrLn ("The square of " ++ input ++ " is " ++ show ...
square :: Int -> Int
square = \x -> x * x
実行例を載せておく。
Input an integer.
440
The square of 440 is 193600.
なお、readをGHCiから単独で使おうとすると、曖昧エラーが発...
Prelude> read "4"
<interactive>:1:0:
Ambiguous type variable `a' in the constraint:
`Read a' arising from a use of `read' at <interact...
Probable fix: add a type signature that fixes these ...
これは、readが実は整数以外の型も扱えるようになっていて、...
readInt :: String -> Int
readInt = read
引数として与えられた文字列が整数として解釈できない場合、r...
*Main> readInt "4"
4
*Main> readInt "-4"
-4
*Main> readInt "4a"
*** Exception: Prelude.read: no parse
:練習問題|標準入力から秒数を受け取って、それが何日と何時...
**真偽値 [#h994bb3d]
プログラムでは、特定の条件が満たされているかどうかによっ...
***if式 [#b6af603d]
次に示すのは、秒数を入力してもらって、それが何日間に相当...
main :: IO ()
main = do
putStrLn "Input a duration in seconds."
input <- getLine
putStrLn (input ++ " seconds = " ++ show (secsToDays (...
secsToDays :: Int -> Int
secsToDays = \secs -> div secs day
day :: Int
day = 60 * 60 * 24
このプログラムを実行し、「100000」を入力すると、「100000 ...
mainの定義にむやみに複雑な式を書くのを避けるため、日数か...
formatDays :: Int -> String
formatDays = \n -> show n ++ " days"
いまのところ、「formatDays 4」は"4 days"に、「formatDays ...
formatDays = \n -> show n ++ if n < 2 then " day" else "...
元の定義と見くらべると、単に「" days"」となっていたところ...
いくつか例を試してみよう。
Prelude> if 1 < 2 then "T" else "F"
"T"
Prelude> if 1 < 1 then 1 else 2
2
Prelude> (if 1 > 3 then 5 else 7) * 9
63
***比較演算 [#hbbe1993]
ここまで、if式の条件部として「n < 2」のような不等式を説明...
Prelude> 1 < 2
True
Prelude> 1 < 1
False
Prelude> 3 + 8 < 9 * 2
True
このように、不等号「<」は通常の演算子で、その結果はTrueか...
if式の条件部には、型がBoolでさえあればどんな式を書いても...
Prelude> if (if True then False else True) then 1 else 2
2
「<」と同じように不等号「>」も使える。また、≦は「<=」、≧...
Prelude> 4 >= 4
True
Prelude> 4 == 4
True
Prelude> 1 + 3 /= 2 * 2
False
***論理演算 [#k41f0cc7]
「xが十進で二桁の自然数である」という条件を表すことを考え...
代わりに、この条件を二つに分解する。「xが10以上である」か...
&&演算子の両辺は真偽値でなければならず、その両方がTrueだ...
(&&) :: Bool -> Bool -> Bool
(||) :: Bool -> Bool -> Bool
not :: Bool -> Bool
この記法について説明しておく。変数定義の場合と同様に、「A...
:練習問題|前に挙げた「名前を尋ねて、その名前で挨拶するプ...
**便利な構文 [#bfa33ef9]
***関数定義 [#v3af4751]
実際のHaskellプログラムでは、関数は極めて頻繁に作られ、関...
次の定義は、整数の符号を反転する関数を、通常の記法で書い...
myNegate :: Int -> Int
myNegate = \n -> 0 - n
専用の構文を使うと、この定義を次のように書ける。
myNegate :: Int -> Int
myNegate n = 0 - n
型指定の部分は変わっていない。違うのは、関数抽象のバック...
引数を複数とる関数も同じように定義できる。次の示すのは、...
diffSquare :: Int -> Int -> Int
diffSquare a b = (a - b) * (a - b)
この構文を使った定義は、局所的・機械的な手順で通常の定義...
***where節 [#m0412c28]
上のdiffSquareの定義を再掲する。
diffSquare :: Int -> Int -> Int
diffSquare a b = (a - b) * (a - b)
この定義には「a - b」という式が二回出現している。これを一...
diffSquare :: Int -> Int -> Int
diffSquare a b = square (a - b)
square :: Int -> Int
square x = x * x
もうひとつ方法がある。''局所変数''を利用するものだ。同じ...
diffSquare :: Int -> Int -> Int
diffSquare a b = d * d
where
d = a - b
この定義の本体は、次の一行だ。
diffSquare a b = d * d
この式ではdという変数を使っている。dの定義は「where」以降...
whereは予約語で、where以降の部分を''where節''という。wher...
文法的には、where節は式ではなく定義に付属する。一つの定義...
where節中で関数定義の構文を使うこともできる。次の例は、引...
hasSmallFactor :: Int -> Bool
hasSmallFactor n = test 2 || test 3 || test 5
where
test k = mod n k == 0
「test k」は、nがkで割り切れるかどうかを返す。これはもち...
定義の右辺が複雑になるとき、式を分割して読み書きしやすく...
if式を使えば、ある年が閏年かどうか判定することができる。...
まず、ある整数が別の整数の倍数であるかどうかを判定する関...
divisible :: Int -> Int -> Bool
divisible n d = mod n d == 0
これを使って、西暦で表された年が閏年かどうかを判定する関...
isLeap :: Int -> Bool
isLeap n = divisible n 4 && (not (divisible n 100) || di...
これは複雑で、一見して何を表しているか分かりにくい。これ...
isLeap :: Int -> Bool
isLeap n = divisible n 4 && not exn -- n年が閏年なのは、...
where -- ただし
exn = divisible n 100 && not exn_of_exn -- 例外とは...
exn_of_exn = divisible n 400 -- 例外の例外とは、nが4...
***ガード [#b0888afb]
if式を使うと、条件に従って二つの選択肢から一つを選ぶ計算...
grade :: Int -> String
grade n =
if 80 <= n
then "優"
else if 70 <= n
then "良"
else if 60 <= n
then "可"
else "不可"
読み易いように複数行に分け、字下げを行なったが、それでも...
grade :: Int -> String
grade n
| 80 <= n = "優"
| 70 <= n = "良"
| 60 <= n = "可"
| True = "不可"
このように、定義の左辺の部分を書いた後、「| 条件 = 本体」...
これらの条件式の一つ一つを''ガード''と呼ぶ。
この例では、最後の条件が常にTrueなので、他の条件が全てFal...
-- 整数の絶対値を計算する
absolute :: Int -> Int
absolute n
| n < 0 = negate n
| otherwise = n
otherwiseは単なる変数で、「otherwise = True」と定義されて...
***ガードとwhere節 [#pec67af2]
ガードを使った定義にwhere節を付ける場合、where節は最後に...
-- 二次方程式 ax^2 + bx + c = 0 の解の個数を計算する
nRoot :: Int -> Int -> Int -> Int
nRoot a b c
| det > 0 = 2
| det == 0 = 1
| otherwise = 0
where
det = b * b - 4 * a * c
**続・コマンド [#m47719aa]
この節では、コマンドやdo式について、まだ説明していなかっ...
***do式の中でif式を使う [#n39b3e2b]
(TODO: 書く)
***return [#ab910692]
returnは「なにもしないで、結果だけ生成するコマンド」を作...
returnを使うと、自分で作ったコマンドがどんな値を結果とす...
foo :: IO Int
foo = do
getLine
return 1
もうすこし実用的な例として、ユーザにyes/noの質問をする関...
ask :: String -> IO Bool
ask question = do
putStrLn question
ln <- getLine
return (ln == "yes")
==演算子は、整数だけでなく様々な値の等しさを判定できる。...
こうして定義したaskは、例えば次のようにして使う。
main :: IO ()
main = do
a1 <- ask "Do you like cheese?"
a2 <- ask "Do you like cake?"
putStrLn
(if a1 && a2
then "Then you like cheesecake!"
else "I have nothing to say about you.")
***let文 [#qb80c1b8]
do式を書いていると、do式内部でのみ使える変数を定義したく...
main :: IO ()
main = do
input <- getLine
putStrLn (show (dig1 (read input)) ++ " + " ++ show (d...
-- 一の位
dig1 :: Int -> Int
dig1 x = mod x 10
-- 十の位
dig10 :: Int -> Int
dig10 x = dig1 (div x 10)
このプログラムを実行して「1234」と入力すれば、「4 + 3 = 7...
''let文''を使うと、このmainの定義を次のように書き直せる。
main :: IO ()
main = do
input <- getLine
let num = read input
let d1 = dig1 num
let d10 = dig10 num
putStrLn (show d1 ++ " + " ++ show d10 ++ " = " ++ sho...
このように、do式中に「let 変数 = 式」という形のものを書く...
なお、''文''とは、do式中に並べることができるもののことだ...
一つのlet文で複数の変数を定義することもできる。この場合、...
main :: IO ()
main = do
input <- getLine
let
num = read input
d1 = dig1 num
d10 = dig10 num
putStrLn (show d1 ++ " + " ++ show d10 ++ " = " ++ sho...
このdo式は三つの文から成り立っている。上から順に、束縛文...
**リスト [#wfdbae40]
プログラミングでは、データの「並び」をひとまとめにして扱...
***リストの表記 [#t2d7ee92]
リストは、式をコンマで区切り、全体を大括弧で括って書き表...
Prelude> [1, 2, 3, 2]
[1,2,3,2]
Prelude> [1 + 2, 3 + 4]
[3,7]
Prelude> [False, False, False, True]
[False,False,False,True]
リストの中身をそのリストの''要素''という。例えば2は[1,2]...
Prelude> [127]
[127]
Prelude> []
[]
あるリストについて、その要素の型は全て同じでないといけな...
**リスト操作の例/データ集計 [#i4a82046]
リストを操作するプログラムの例として、データ(たとえば気温...
入力データは、カレントディレクトリにinput.txtという名前で...
3
1
-1
-3
-2
この状態でプログラムを実行すると-1が表示される(平均は-0.4...
***平均を計算する関数 [#hbc242db]
このプログラムの肝は整数の列の平均を取るところにあるので...
整数のリストから平均を計算するには、要素の合計を要素数で...
Prelude> length [2,3,4]
3
Prelude> length []
0
また、要素の合計は関数sumで得られる。
Prelude> sum [2,3,4]
9
Prelude> sum []
0
だから、整数のリストからその平均値への関数averageは次のよ...
average :: [Int] -> Int
average xs = div (sum xs) (length xs)
試してみよう。
*Main> average [2, 5, 9]
5
*Main> average [-1, -2]
-2
*Main> average [99]
99
averageの引数が長さ零のリスト(''空リスト''という)なら、エ...
*Main> average []
*** Exception: divide by zero
***入出力を書く [#u30a2a1e]
テキストファイルの中身を読むには、readFileという関数を使...
readFile :: String -> IO String
readFileの引数としてファイル名を指定すると、そのファイル...
結果の表示にはputStrLnを使えばいいだろう。だから、mainの...
main :: IO ()
main = do
content <- readFile "input.txt"
putStrLn (process content)
ただし、processはこれから定義する関数で、ファイルの内容を...
既に見たように、ファイルの内容は"3\n1\n-1\n-3\n-2\n"のよ...
Prelude> lines "abc\ndef\n"
["abc", "def"]
Prelude> lines "stream and\npipelining"
["stream and", "pipelining"]
Prelude> lines "3\n1\n-1\n-3\n-2\n"
["3", "1", "-1", "-3", "-2"]
これで["3", "1", "-1", "-3", "-2"]のような文字列のリスト...
*Main> map show [1, 2, -3]
["1","2","-3"]
*Main> map length ["foo", "bar", "baz", "quux"]
[3,3,3,4]
*Main> map (\x -> x + 1) [1, 2, -3]
[2,3,-2]
*Main> map readInt ["3", "1", "-1", "-3", "-2"]
[3,1,-1,-3,-2]
以上を組み合せると、processは次のように定義できる。
process :: String -> String
process input = show (average (map read (lines input)))
**リスト操作の例/空行の削除 [#e0d19c75]
もう一つリスト操作をするプログラムの例をみておこう。input...
first line
second line
この場合、出力は次のようになってほしい。
first line
second line
***データの流れ [#cde4de32]
ファイルの内容を読み込んで、それをlinesで行単位に分割する...
Prelude> unlines ["ab", "c", "", "de"]
"ab\nc\n\nde\n"
unlinesはlinesのほぼ逆((元の文字列xが空でなく、しかも改行...
+linesで入力を行ごとに分割したリストを得る。入力文字列が"...
+この行のリストから、空文字列を除いたものを得る。この例な...
+これにunlinesを適用して、結果を得る。これは"first line\n...
mainは次のように定義できる。ただし、removeEmpties :: [Str...
main :: IO ()
main = do
input <- readFile "input.txt"
putStr (unlines (removeEmpties (lines input)))
putStrは、putStrLnと似ているが、文字列を出力するだけで、...
***filter [#z0f13cfa]
removeEmptiesを定義することを考えよう。このように、リスト...
Prelude> filter even [1, 2, 3, 4, 99]
[2,4]
Prelude> filter odd [1, 2, 3, 4, 99]
[1,3,99]
ここで、evenは「偶数である」という条件、oddは「奇数である...
evenやoddの正体は、単に「真偽値を返す関数」だ。
Prelude> even 2
True
Prelude> even 3
False
Prelude> even 4
True
このように、「偶数であればTrue、そうでなければFalseを返す...
Prelude> (\x -> x > 10) 5
False
Prelude> (\x -> x > 10) 10
False
Prelude> (\x -> x > 10) 12
True
Prelude> filter (\x -> x > 10) [9, 10, 12, 24]
[12,24]
removeEmptiesを定義する問題に戻ろう。removeEmptiesは、元...
removeEmpties :: [String] -> [String]
removeEmpties ls = filter (\x -> length x /= 0) ls
試してみる。
*Main> removeEmpties ["a", "^^", "", " ", "#########", ""]
["a","^^"," ","#########"]
**さらにリスト [#x482d57f]
この節では、リスト操作関数のうち、ここまでに紹介できなか...
***(++)とconcat [#da52391d]
++演算子は、二つのリストを連結したリストを返す。
Prelude> [True, False] ++ [False]
[True,False,False]
concatはリストのリストを受けとり、その要素を全て連結した...
Prelude> concat [[1, 2], [], [3], [4, 5, 6]]
[1,2,3,4,5,6]
Prelude> concat [[[1], []], [[2,3]]]
[[1],[],[2,3]]
***reverse [#sb2f2265]
reverse xはxを逆順にしたリストになる。
Prelude> reverse [1,2,3,4]
[4,3,2,1]
Prelude> reverse [[1,2], [3,4]]
[[3,4],[1,2]]
***replicate [#b8c18f63]
replicate n xはn個のxを要素とするリストを作る。
Prelude> replicate 5 7
[7,7,7,7,7]
Prelude> replicate 3 True
[True,True,True]
***数列表記 [#mc890872]
[1,2,3,4,5]のような連続する整数のリストは、専用の記法で生...
Prelude> [1..5]
[1,2,3,4,5]
Prelude> [2..2]
[2]
Prelude> [2..1]
[]
**文字 [#b4270166]
***文字のリスト [#w8e80007]
lengthや(++)といった関数が文字列にもリストにも使えること...
Haskellで文字を表すには、その文字を一重引用符で括ればよい...
文字の型はCharと書く。文字列は文字のリストだから、String...
同様に、例えば"abc"と['a', 'b', 'c']は同じ値を表している...
Prelude> ['f', 'u', 'l', 'l']
"full"
文字列は文字のリストなので、リスト操作関数の多くは文字列...
Prelude> concat ["foo", show (1+1), "bar"]
"foo2bar"
Prelude> reverse "Dog"
"goD"
***型シノニム [#g96968d6]
Stringは[Char]の別名だが、このような型に対する別名(''型シ...
type String = [Char]
次の例では、整数から真偽値への関数に「IntPred」という別名...
-- 整数から真偽値への関数、あるいは整数の「性質」
type IntPred = Int -> Bool
-- 二桁の整数であるという性質
doubleDigit :: IntPred
doubleDigit x = 10 <= x && x < 100
-- 二つの性質を両方満たすという性質
andp :: IntPred -> IntPred -> IntPred
andp p q = \x -> p x && q x
-- 1から100までの整数で、与えられた性質を満たすものがい...
count100 :: IntPred -> Int
count100 f = length (filter f [1..100])
main :: IO ()
main = do
putStrLn ("count100 doubleDigit = " ++ show (count100 ...
putStrLn ("count100 even = " ++ show (count100 even)) ...
putStrLn ("count100 (andp even doubleDigit) = " ++ sho...
**型 [#df8d45a5]
ここまで、型についてはいい加減な記述で逃げてたが、この節...
***静的な型付け [#qfcbaeb1]
全ての値には型がある。例えば"redone"の型はStringだし[True...
式の型を計算することを''型付け''という。型付けは失敗する...
Haskellでは、プログラムの実行が始まるより前に、プログラム...
これは、ghcコマンドでプログラムをコンパイルする場合が分か...
main :: IO ()
main = putStrLn (if not "鯖" then "2" else "1")
これを、例えばtypeerror.hsという名前を付けて保存し、ghcコ...
$ ghc --make typeerror.hs
[1 of 1] Compiling Main ( typeerror.hs, type...
typeerror.hs:2:24:
Couldn't match expected type `Bool' against inferred...
In the first argument of `not', namely `"\39894"'
In the predicate expression: not "\39894"
In the first argument of `putStrLn', namely
`(if not "\39894" then "2" else "1")'
このように、コンパイルの段階で型検査が行われ、エラーがあ...
一方、整数を0で割ろうとしたり、ガードを使った定義で条件を...
main :: IO ()
main = putStrLn (show (div 1 0))
これをghcコマンドでコンパイルすると、コンパイルは成功して...
***静的型付けによる制限 [#sbb19355]
Haskellでは、評価が始まる前に式が型検査されるので、式の評...
v :: Int
v = if 1 > 0 then 1 else "hell"
このif式の条件は必ず満たされるが、それは「1 > 0」を評価し...
この理由から、if式のthen部とelse部は同じ型の式でないとい...
***多相型 [#xe043929]
lengthの型について考えてみる。lengthは一引数の関数で、結...
Prelude> length [3, 3, 1]
3
Prelude> length [True, True, True]
3
Prelude> length [length, length]
2
GHCiには:type(省略形は:t)というコマンドがあって、式の型を...
Prelude> :t "ray"
"ray" :: [Char]
Prelude> :t 1 > 2
1 > 2 :: Bool
これを使ってlengthの型を調べてみると、以下のようになる。
Prelude> :t length
length :: [a] -> Int
「a」の部分は''型変数''といって、その部分を好きな型に置き...
型変数の名前はなんでもいい。例えば「length :: [b] -> Int...
型変数を含む型を''多相型''という。多相型を持つ値を''多相...
もう少し複雑な例を見てみよう。replicateの型は以下のとおり。
replicate :: Int -> a -> [a]
この型では型変数aが二回使われている。この場合もaは好きな...
複数の型変数を含む型もある。mapの型がその例だ。
map :: (a -> b) -> [a] -> [b]
例えば、map show [1, 2]という使い方では、aがIntに、bがStr...
***多相的な変数の定義 [#t4d025e5]
自分で多相的な変数を定義することができる。特別な記法は必...
mirror :: [a] -> [a]
mirror xs = xs ++ reverse xs
この関数は次のように使える。
*Main> mirror [1]
[1,1]
*Main> mirror "te"
"teet"
必要なら、より特殊な(一般的でない)型を指定することもでき...
mirror :: [Int] -> [Int]
***型推論 [#ndf5bdb9]
これまで、局所的でない定義(''最上位''の定義という)では、...
rjust :: Int -> String -> String -- ←これが型シグネチャ
rjust n s = spaces ++ s
where
-- ここにspacesの型シグネチャがない
spaces = replicate (n - length s) ' '
実際には、特殊な状況を除いて、型シグネチャは書いても書か...
最上位の変数については、推論の結果をGHCiから確認できる。...
mirror xs = xs ++ reverse xs
これをGHCiにロードし、:tコマンドで型を確認する。
*Main> :t mirror
mirror :: [a] -> [a]
正しい型が推論されている。
このように、型シグネチャを書くことは必須でないので、型シ...
-意図が明確になる。型の情報が書かれていると、その変数がど...
-エラーメッセージが分かり易くなる。変数の定義中で記述ミス...
この文書では、原則として、最上位の定義には型シグネチャを...
**型クラス [#v120721a]
***制約 [#r95dc2a7]
前に紹介したように、==演算子を使うと整数と整数を比較でき...
Prelude> 1 == 1
True
Prelude> 1 == 1 + 1
False
==演算子で比較できるのは実は整数だけではない。次のように...
Prelude> True == False
False
Prelude> '\10' == '\n'
True
Prelude> "tanasin" == "tanasinn"
False
Prelude> () == ()
True
Prelude> "f" == 'f'
<interactive>:1:7:
Couldn't match expected type `[Char]' against inferr...
In the second argument of `(==)', namely 'f'
In the expression: "f" == 'f'
In the definition of `it': it = "f" == 'f'
一方、比較できない型もある。関数やIOコマンドがその一例だ。
Prelude> length == length
<interactive>:1:0:
No instance for (Eq ([a] -> Int))
arising from a use of `==' at <interactive>:1:0-15
Possible fix: add an instance declaration for (Eq ([...
In the expression: length == length
In the definition of `it': it = length == length
Prelude> putStrLn "hello" == putStrLn "hello"
<interactive>:1:0:
No instance for (Eq (IO ()))
arising from a use of `==' at <interactive>:1:0-35
Possible fix: add an instance declaration for (Eq (I...
In the expression: putStrLn "hello" == putStrLn "hel...
In the definition of `it':
it = putStrLn "hello" == putStrLn "hello"
つまり、(==)は「a -> a -> Bool」という型を持つように振る...
(==)の型は次のように書く。
(==) :: Eq a => a -> a -> Bool
「Eq a」の部分が制約を表している。これは「型変数aは、Eqと...
「等値比較ができる」という性質を持つ型は具体的にはInt、Bo...
クラスの名前は大文字で始まる。
以下では、普段のプログラミングで頻繁に使うことになるだろ...
***Eq [#r5e4e8eb]
Eqは、(==)や(/=)で比較できる型を表す。Int、Bool、Charとい...
Eq制約を持つ関数の例としてelemがある。elemは、リストの中...
Prelude> :t elem
elem :: (Eq a) => a -> [a] -> Bool
Prelude> elem 3 [2,4,6]
False
Prelude> elem ' ' "second p"
True
***Ord [#xf10abbb]
Eqが「等しいかどうか」の判定を許す型のクラスであるのに対...
Int、Bool、CharはOrdのインスタンスだ。Charについては、Uni...
Eqの場合と同じく、リストは、要素の型がOrdのインスタンスで...
maxとminという関数は、二つの値から大きい方または小さい方...
Prelude> :t max
max :: (Ord a) => a -> a -> a
Prelude> max 4 5
5
Prelude> min "tree" "root"
"root"
一つのリストの要素のなかで最大のものと最小のものを得るに...
Prelude> :t maximum
maximum :: (Ord a) => [a] -> a
Prelude> maximum [10, 3, 7]
10
Prelude> minimum (map (\x -> x * (x - 7)) [1..100])
-12
Prelude> maximum []
*** Exception: Prelude.maximum: empty list
Ordの典型的な利用法として、リストを並べ換えるというのがあ...
ソースファイル中でsortを使うには、ファイルの先頭に次の一...
import List
これを''import宣言''という。これが必要なのは、sortがList...
GHCiでは、「import List」または「:module +List」と入力す...
Prelude> :t sort
<interactive>:1:0: Not in scope: `sort'
Prelude> import List
Prelude List> :t sort
sort :: (Ord a) => [a] -> [a]
これで、sort関数を試してみることができる。
Prelude List> sort [3,8,7,-1,99]
[-1,3,7,8,99]
Prelude List> sort ["az", "a", "ba", "bz", "b"]
["a","az","b","ba","bz"]
なお、lengthや(+)などの変数、IntやStringなどの型はimport...
***ShowとRead [#f7929d41]
showとreadも整数以外の多くの型に対して使える。対応するク...
show :: (Show a) => a -> String
read :: (Read a) => String -> a
Prelude> show 1
"1"
Prelude> show True
"True"
Prelude> show otherwise
"True"
Prelude> show [2, 3, 5]
"[2,3,5]"
Prelude> show 'c'
"'c'"
Prelude> show "string"
"\"string\""
実は、GHCiは(IOコマンド以外の)評価結果を表示する際にこのs...
Showクラスの利用例としてprint関数がある。printは、引数を...
print :: (Show a) => a -> IO ()
***文脈 [#q181a7cc]
一つの型シグネチャに複数の制約が必要なこともある。複数の...
makeList :: (Show a, Ord a) => [a] -> String
makeList xs = unlines (map show (sort xs))
このような制約の並び(この例では「(Show a, Ord a)」の部分)...
***クラス制約の曖昧さ [#i206d8ad]
多相的な変数のうち、型にクラス制約を含むもの(show、max、s...
このため、多重定義された変数については、その変数が具体的...
reshow :: String -> String
reshow x = show (read x)
この場合、readの結果の型とshowの引数の型が決まらないので...
このような場合、式の型を明示することで曖昧さを取り除くこ...
reshow :: String -> String
reshow x = show (read x :: Int)
式の型を明示するには「式 :: 型」という形の式を使う。定義...
なお、このように曖昧エラーになるのは、多重定義された変数...
decorate :: (Show a) => a -> String
decorate v = "<" ++ show v ++ ">"
この関数定義では、showの引数であるvの型がまだ確定していな...
**タプル [#ea360212]
***何それ [#hc7dcdae]
(TODO:タプルが欲しくなる例を書く)
''タプル''は複数の値をまとめたものだ。タプルは、値をコン...
Prelude> (1, True)
(1,True)
Prelude> (3 + 4, 3 < 4)
(7, False)
Prelude> ("z", True, "foo")
("z",True,"foo")
タプルの型は、要素の型をコンマで区切って、全体を括弧で囲...
***パターン照合 [#mc98f129]
タプルが与えられたとき、その要素の値を取り出すには''パタ...
v :: (Int, String)
v = (4, "Rich")
このように定義すると、vは(4, "Rich")に束縛される。
次に、これを以下のように書き換える。
a :: Int
b :: String
(a, b) = (4, "Rich")
こうすると、aは4に、bは"Rich"に束縛される。このように、定...
変数を束縛できる構文の多くでは、変数の代わりにパターンを...
sum3 :: (Int, Int, Int) -> Int
sum3 tup = x + y + z
where
(x, y, z) = tup
関数の仮引数としてパターンを直接書いてもいい。sum3は次の...
sum3 :: (Int, Int, Int) -> Int
sum3 (x, y, z) = x + y + z
***fstとsnd [#f9ab5b55]
このように、タプルの内容はパターン照合で取り出すのが普通...
fst :: (a, b) -> a
snd :: (a, b) -> b
fstは対の第一要素、sndは第二要素を返す。
Prelude> fst (True, 44)
True
Prelude> snd (True, 44)
44
*リンク [#d251be92]
**公式 [#s71afa0c]
-http://www.haskell.org/
**処理系 [#je126b42]
:GHC|http://www.haskell.org/ghc/
:Hugs|http://www.haskell.org/hugs/
**その他 [#e7d4d60c]
:[[The Haskell 98 Report (Revised):http://haskell.org/onl...
:[[Foreign Function Interface:http://www.cse.unsw.edu.au/...
:[[Hierarchical modules:http://www.haskell.org/hierarchic...
:[[The Glorious Glasgow Haskell Compilation System User's...
:[[Haskell Hierarchical Libraries:http://www.haskell.org/...
:[[HackageDB:http://hackage.haskell.org/packages/hackage....
:[[Hoogle:http://www.haskell.org/hoogle/]]|ライブラリ関数...
**日本語サイト [#cd41828e]
:[[Programming in Haskell:http://www.sampou.org/cgi-bin/h...
:[[Haskell 98 言語とライブラリ 改訂レポート:http://www.sa...
:[[栄光のグラスゴーHaskellコンパイルシステム利用の手引き:...
終了行:
*Haskell [#d5adcc0a]
#contents
*能書き [#ycc6155f]
**Haskellの特徴 [#nf0358a2]
Haskellのおもな特徴を列挙すると次のようになる。この手の言...
-代数的データ型と、パターン照合。
-インデントを利用した簡潔な構文。
-型推論つき静的型付け。
-型推論と多重定義を両立する、型クラス。
-純粋な(つまり、式に副作用がない)関数型であること。
-第一級の動作による入出力。
**関数型言語って何 [#l5605168]
Haskellは純粋関数型言語に分類される。関数型言語というのは...
現在主流の言語は、ほぼ全て手続き型言語だ。手続き的プログ...
1からnまでの整数の中で、2でも3でも割り切れないものの総和...
整数を6で割った余りに着目する賢い方法もあるが、ここでは素...
「総和」を0とする。
「現在の整数」が0からnまで動く間、以下の一行を繰り返す:
もし、「現在の整数」が2でも3でも割り切れないなら、「総...
この時点で「総和」が問題の答えになっている。
このプログラムには、「「総和」を0とする」とか、「「総和」...
一方、関数的プログラミングでは、データに着目する。何か問...
1からnまでの整数の列をAとする。
Aから、2でも3でも割り切れない整数のみを取り出した列をBと...
Bの要素を全て足し合わせたものが問題の答えである。
このプログラムには命令はどこにも出てこない。ただ入力と出...
このふたつのスタイルの違いは、言語の違いにそのまま対応す...
手続き型言語は、現実のコンピュータと相性の良い構造になっ...
**おすすめ [#x0716709]
上のような特徴から、少なくとも以下のような人にはHaskellを...
-手続き型言語に飽きた。
-命令するのよりも記述する方が好き。
-複雑な記号操作を必要とするプログラム(言語処理系とか)を書...
-俗世よりも抽象的・記号的な世界に興味を惹かれる。数学好き...
*処理系を導入する [#bc9decb0]
**入れる処理系を選ぶ [#p188b359]
2011年6月の時点では、以下の処理系・パッケージがメンテナン...
:[[Haskell Platform:http://hackage.haskell.org/platform/]...
:[[GHC:http://www.haskell.org/ghc/]]|対話環境とインタプリ...
:[[Hugs:http://www.haskell.org/hugs/]]|対話環境とインタプ...
ここでは、利用者の多いと思われるGHCのインストールを説明す...
**GHCのインストール [#n589bef9]
***Windows [#hc33c8f1]
[[GHCのダウンロードページ:http://www.haskell.org/ghc/down...
コマンドプロンプトを開き、「ghc --version」と入力してみる。
The Glorious Glasgow Haskell Compilation System, version...
こんな感じのものが表示されれば成功。
***UNIX互換OS [#e399dfbb]
OS、ディストリビューションごとにパッケージが用意されてい...
***その他のプラットフォーム [#f94399f7]
[[GHCのダウンロードページ:http://www.haskell.org/ghc/down...
*チュートリアル [#sbc437ae]
**式 [#n212859c]
プログラムは計算をしたり、それ以外のことをしたりするが、H...
***対話環境 [#s3caf7cf]
実際にHaskellで計算をしてみよう。まずGHC付属の対話環境、G...
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for...
Loading package base ... linking ... done.
Prelude>
最後の行は「Prelude>」となっていて、行末にカーソルがあり...
Prelude> 33 + 34
67
Prelude>
こちらの「33 + 34」という入力に対して、GHCiは「67」という...
さて、この場合、GHCiは「33 + 34」を計算して結果「67」を得...
GHCiを終了させたいときは「:quit」または短縮して「:q」と入...
***演算子と関数 [#a137e737]
Haskellでは足し算以外にも多くの種類の式を扱うことが出来る...
まず、足し算と同じように引き算も使える。
Prelude> 33 - 34
-1
この二つは自由に組み合わせることができる。
Prelude> 3 + 6 - 1 - 4
4
スペースはあってもなくても良い。
Prelude> 3+6 - 1- 4
4
単独の整数は、それ自体で式になる。
Prelude> 7
7
掛け算は「*」で表す。
Prelude> 2 * 7
14
数学と同じように、掛け算は加減算よりも優先される。これに...
Prelude> 1 + 2 * 3
7
Prelude> (1 + 2) * 3
9
括弧を多重に使いたいときは、そのまま入れ子にすれば良い。
Prelude> (14 - (1 + 2)) * 4
44
「+」とか「-」とか「*」のような記号を''演算子''と呼ぶ。
除算は少し勝手が違う。整数の除算の結果は商と余りの二つで...
Prelude> div 7 3
2
Prelude> mod 7 3
1
divやmodは''関数''だ。Haskellにおいて関数とは、数学でいう...
Haskellで関数適用を表現する時は、「div 7 3」のように関数...
Prelude> div 7 3 + 1
3
Prelude> div 7 (3 + 1)
1
divやmodは引数を二つ取る関数だが、そうでない関数もある。...
Prelude> negate 4
-4
Prelude> negate (2 - 5)
3
入力の値から出力の値が決まる、という点では、演算子は関数...
Prelude> (+) 1 2
3
Prelude> (-) ((*) 3 4) 7
5
***エラー [#pf637a00]
入力する式を打ち間違えると、GHCiはエラーメッセージを出力...
式が入力されると、GHCiはまず式の構造を解析する。どのカッ...
Prelude> 4 +
<interactive>:1:3: parse error (possibly incorrect inden...
Prelude> (2 + 3)) * 4
<interactive>:1:7: parse error on input `)'
式が構文解析に成功して、GHCiが構造を把握すると、次に「型...
Prelude> div + mod
<interactive>:1:0:
No instance for (Num (a -> a -> a))
arising from use of `+' at <interactive>:1:0-8
Possible fix: add an instance declaration for (Num (...
In the expression: div + mod
In the definition of `it': it = div + mod
「No instance for...」というエラーメッセージが型エラーを...
構文的ミスによって型エラーが起きることもある。括弧をつけ...
Prelude> div div 34 3 2
<interactive>:1:0:
No instance for (Integral (a -> a -> a))
arising from use of `div' at <interactive>:1:0-13
Possible fix:
add an instance declaration for (Integral (a -> a ...
In the expression: div div 34 3 2
In the definition of `it': it = div div 34 3 2
この場合、関数「div」がdiv, 34, 3, 2という4つの引数を伴っ...
型検査に成功すれば、正しいHaskellの式だと確認されたことに...
Prelude> div 6 (3 - 3)
*** Exception: divide by zero
評価中に発生したエラーは「*** Exception: ...」と報告され...
なお、例えば「div」を単独で評価しようとすると以下のように...
Prelude> div
<interactive>:1:0:
No instance for (Show (a -> a -> a))
arising from a use of `print' at <interactive>:1:0-2
Possible fix: add an instance declaration for (Show ...
In the expression: print it
In a 'do' expression: print it
これは「div」が式として間違っているというのではなく、「di...
**定義 [#r22e0d79]
***ソースファイルと変数 [#e2982bf8]
なんらかの理由で、百万秒が何日間に相当するか知りたくなっ...
Prelude> 60 * 60 * 24 -- 一分が60秒で、一時間が60分で、...
86400
Prelude> div 1000000 86400 -- 一日が86400秒だから、百万...
11
これで、端数を切り捨てて十一日だということが分かった。な...
では、逆に、14日間は何秒かを知りたくなったとしよう。一日...
Prelude> 14 * 86400
1209600
もし、この手の計算を何度もしなければならないなら、その都...
値に名前を付けたもののことを''変数''という。変数を使うに...
Haskellソースファイルの中身はテキストファイルだが、拡張子...
day :: Int
day = 86400
これをGHCiから使うには、:loadコマンド(省略形は:l)でロード...
Prelude> :load day.hs
[1 of 1] Compiling Main ( day.hs, interprete...
Ok, modules loaded: Main.
*Main>
GHCiをコマンドラインから起動しているなら、起動時の引数と...
これで、式のなかで86400の代わりにdayという変数を使えるよ...
*Main> day
86400
*Main> day + 1
86401
*Main> div 8000000 day
92
これでいちいち長い数値をタイプする必要はないし、この数値...
なお、day.hsをロードした状態でこれを編集したら、:reloadコ...
さて、ソースファイルの中身を解説しよう。day.hsを再掲する。
day :: Int
day = 86400
二行目は、変数dayを、値86400を持つ変数として定義する、と...
今回、一日の秒数をGHCiで予め計算して、その値を使ってdayを...
day :: Int
day = 60 * 60 * 24
一つのソースファイルには、いくつでも変数の定義を書くこと...
-- 一分の秒数
minute :: Int
minute = 60
-- 一時間の秒数
hour :: Int
hour = minute * 60
-- 一日の秒数
day :: Int
day = hour * 24
このように、ソースファイル中には、コメントや空行を自由に...
複数の定義を書く場合、順番はどうでもいい。例えば、上のフ...
-- 一日の秒数
day :: Int
day = hour * 24
-- 一時間の秒数
hour :: Int
hour = minute * 60
-- 一分の秒数
minute :: Int
minute = 60
***関数 [#n0fdbe51]
ここまでに定義したdayとhourを使えば、例えば220万秒が何日...
*Main> div 2200000 day -- 220万秒は何日か?
25
*Main> mod 2200000 day -- 残りの秒数は?
40000
*Main> div 40000 hour -- 40000秒は何時間か?
11
よって、220万秒は25日と11時間だ。しかし、秒数が何日何時間...
このような機能は、関数を使うとうまく表現できる。前に説明...
この関数の結果は、例えば引数が100000ならdiv 100000 day、...
\secs -> div secs day
つまり、関数を作るには、まず、何か変数を適当に決めて(この...
ここでのsecsのような変数を''仮引数''という。仮引数の名前...
さて、こうして作った関数は、通常の関数と全く同じように適...
*Main> (\secs -> div secs day) 2200000
25
*Main> (\secs -> div secs day) 1200000
13
これで無事に関数を作ることができたので、あとは名前を付け...
secsToDays :: Int -> Int
secsToDays = \secs -> div secs day
二行目はいつもどおりの変数定義だ。一行目で「Int -> Int」...
これをGHCiにロードすれば、秒数から日数への変換が簡単にで...
*Main> secsToDays 1234567
14
*Main> secsToDays 6666666
77
:練習問題|与えられた秒数が何日何時間に相当するか、という...
***多引数関数 [#bf01cb50]
ついでに、引数を二つ以上取る関数の書き方も見ておこう。関...
\x y -> div (x + y) 2
この関数の型は「Int -> Int -> Int」になる。一般に、N引数...
**プログラム [#n31e6651]
これで準備が整ったので、実際に動作するプログラムを書く方...
***Hello, world [#q5fd2d0d]
次のソースコードをhello.hsとしよう。これは、標準出力に「H...
main :: IO ()
main = putStrLn "Hello, world"
コマンドラインで、hello.hsのあるディレクトリに移動し、次...
ghc --make hello.hs
すると、メッセージが表示され、同じディレクトリにhello(Win...
なお、Windowsで、エクスプローラからhello.exeを起動すると...
今、ghcコマンドで実行ファイルを生成してからそれを実行する...
$ runghc hello.hs
Hello, world
さて、hello.hsを解説しよう。再掲する。
main :: IO ()
main = putStrLn "Hello, world"
前節でみたとおり、これは「main」という変数を一つ定義する...
mainの型は「IO ()」と指定されている。これはmainの値が''IO...
Haskellプログラムが実行されるときは、変数mainの値がコマン...
このプログラムでは、mainの値は「putStrLn "Hello, world"」...
"Hello, world"というのは、そのまま「Hello, world」という...
putStrLnはコマンドを作る関数だ。引数が文字列xなら、結果は...
なお、putStrLnは「文字列を出力して改行せよ」というコマン...
***コマンドの合成 [#h47be662]
putStrLnを使えば、好きな文字列を標準出力に表示して改行す...
main :: IO ()
main = putStrLn "1st line" >> putStrLn "2nd line"
このプログラムを実行すると、「1st line」と表示して改行し...
合成されたコマンドもまたコマンドなので、新たな合成の材料...
このようなコマンドの合成は頻繁に使われるので、Haskellには...
main :: IO ()
main = do
putStrLn "1st line"
putStrLn "2nd line"
doという単語に続けて複数の式(ここでは二つ)を並べると、全...
式を並べるとき、以下の二つの規則に従う必要がある。
- 一行に一つの式を書くこと。ただし、途中に空行やコメント...
- 式の開始位置を揃えること。この例では、二つの式がどちら...
これさえ守れば、字下げの量は自由だ。例えば、半角スペース...
main :: IO ()
main = do
putStrLn "1st line"
putStrLn "2nd line"
doの直後で改行しないスタイルもある。
main :: IO ()
main = do putStrLn "1st line"
putStrLn "2nd line"
また、長い式を複数行に分けて書きたい場合、ある行を標準よ...
do
a
b c d
e f
g h i
j
do
a
b c d e f g h i
j
これらの規則を''配置規則''といって、Haskellではdo式の他に...
以上から分かるように、「do」という単語は文法上で特別の意...
case class data default deriving do else foreign if impo...
***コマンドの結果 [#wd034879]
プログラムが何か役に立つことをしようと思ったら、外部から...
getLineは標準入力から一行読むコマンドだ。このコマンドが実...
実際にgetLineを実行してみよう。
main :: IO String
main = getLine
このプログラムを実行すると、プログラムはすぐに入力待ちに...
今はgetLineの結果を捨てたが、これを利用することを考えよう...
>>=演算子は>>に似ていて、左辺を実行してから右辺を実行...
今、getLineの結果を使ってするべきことは、それを表示して改...
main :: IO ()
main = getLine >>= putStrLn
これを実際に実行して、入力した文字列がそのまま表示される...
具体的に何が起こるか見ておこう。「getLine >>= putStrLn」...
この種の合成も、do式を使って書ける。例えば、このプログラ...
main :: IO ()
main = do
line <- getLine
putStrLn line
一般に、do式中に式を並べるとき、式を書く代わりに「変数 <-...
ところで、上で「T型の結果を生成するコマンドの型はIO Tだ」...
***GHCiとコマンド [#h1dcd27b]
コマンドは、関数と同じく、表示できないデータだ。しかし、G...
Prelude> putStrLn "Helium"
Helium
これは、GHCiがコマンドを特別扱いするためだ。通常、GHCiは...
Prelude> getLine
infornography
"infornography"
これは便利な機能だが、慣れないうちは混乱の原因になること...
**文字列 [#jc1a35cf]
文字列は、外部とのやりとりをするのに重要なデータだ。標準...
***文字列リテラル [#n7cbaa1c]
前に紹介したように、文字列とは文字が0個以上並んだものだ。...
Haskellでいう文字とは、Unicodeコードポイントのことだ。厳...
このような目に見える文字の他に、特殊な効果を持つ文字群が...
既に使っているが、プログラム中で文字列の値を書き表すには...
文字列リテラル中に制御文字を直接書くことはできない。例え...
s :: String
s = "温情
判決"
これは、次のように書かないといけない。
s :: String
s = "温情\n判決"
このように、制御文字を含んだ文字列リテラルを書きたいとき...
Unicodeコードポイントを直接番号で指定することもできる。例...
Prelude> "用心棒"
"\29992\24515\26834"
なお、ソースファイル中のコメント以外の場所でASCIIの範囲外...
***文字列操作 [#rf7f4deb]
演算子++を使うと、文字列を連結できる。
Prelude> "rear" ++ "range"
"rearrange"
これを使うと、例えば、名前を尋ねて、その名前で挨拶するプ...
main :: IO ()
main = do
putStrLn "What's your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ ".")
関数lengthは、文字列の長さ(文字数)を返す。
Prelude> length "天上天下唯我独尊"
8
Prelude> length "a\nb"
3
***整数と文字列の相互変換 [#g085d877]
show関数を使うと、整数を10進表記の文字列に変換できる。
Prelude> show (7 * 8)
"56"
これを使うと、整数を使って計算し、その計算結果を出力する...
day :: Int
day = 60 * 60 * 24
main :: IO ()
main = putStrLn ("1 day = " ++ show day ++ " seconds.")
read関数は逆に、10進表記された文字列を整数に変換する。例...
main :: IO ()
main = do
putStrLn "Input an integer."
input <- getLine
putStrLn ("The square of " ++ input ++ " is " ++ show ...
square :: Int -> Int
square = \x -> x * x
実行例を載せておく。
Input an integer.
440
The square of 440 is 193600.
なお、readをGHCiから単独で使おうとすると、曖昧エラーが発...
Prelude> read "4"
<interactive>:1:0:
Ambiguous type variable `a' in the constraint:
`Read a' arising from a use of `read' at <interact...
Probable fix: add a type signature that fixes these ...
これは、readが実は整数以外の型も扱えるようになっていて、...
readInt :: String -> Int
readInt = read
引数として与えられた文字列が整数として解釈できない場合、r...
*Main> readInt "4"
4
*Main> readInt "-4"
-4
*Main> readInt "4a"
*** Exception: Prelude.read: no parse
:練習問題|標準入力から秒数を受け取って、それが何日と何時...
**真偽値 [#h994bb3d]
プログラムでは、特定の条件が満たされているかどうかによっ...
***if式 [#b6af603d]
次に示すのは、秒数を入力してもらって、それが何日間に相当...
main :: IO ()
main = do
putStrLn "Input a duration in seconds."
input <- getLine
putStrLn (input ++ " seconds = " ++ show (secsToDays (...
secsToDays :: Int -> Int
secsToDays = \secs -> div secs day
day :: Int
day = 60 * 60 * 24
このプログラムを実行し、「100000」を入力すると、「100000 ...
mainの定義にむやみに複雑な式を書くのを避けるため、日数か...
formatDays :: Int -> String
formatDays = \n -> show n ++ " days"
いまのところ、「formatDays 4」は"4 days"に、「formatDays ...
formatDays = \n -> show n ++ if n < 2 then " day" else "...
元の定義と見くらべると、単に「" days"」となっていたところ...
いくつか例を試してみよう。
Prelude> if 1 < 2 then "T" else "F"
"T"
Prelude> if 1 < 1 then 1 else 2
2
Prelude> (if 1 > 3 then 5 else 7) * 9
63
***比較演算 [#hbbe1993]
ここまで、if式の条件部として「n < 2」のような不等式を説明...
Prelude> 1 < 2
True
Prelude> 1 < 1
False
Prelude> 3 + 8 < 9 * 2
True
このように、不等号「<」は通常の演算子で、その結果はTrueか...
if式の条件部には、型がBoolでさえあればどんな式を書いても...
Prelude> if (if True then False else True) then 1 else 2
2
「<」と同じように不等号「>」も使える。また、≦は「<=」、≧...
Prelude> 4 >= 4
True
Prelude> 4 == 4
True
Prelude> 1 + 3 /= 2 * 2
False
***論理演算 [#k41f0cc7]
「xが十進で二桁の自然数である」という条件を表すことを考え...
代わりに、この条件を二つに分解する。「xが10以上である」か...
&&演算子の両辺は真偽値でなければならず、その両方がTrueだ...
(&&) :: Bool -> Bool -> Bool
(||) :: Bool -> Bool -> Bool
not :: Bool -> Bool
この記法について説明しておく。変数定義の場合と同様に、「A...
:練習問題|前に挙げた「名前を尋ねて、その名前で挨拶するプ...
**便利な構文 [#bfa33ef9]
***関数定義 [#v3af4751]
実際のHaskellプログラムでは、関数は極めて頻繁に作られ、関...
次の定義は、整数の符号を反転する関数を、通常の記法で書い...
myNegate :: Int -> Int
myNegate = \n -> 0 - n
専用の構文を使うと、この定義を次のように書ける。
myNegate :: Int -> Int
myNegate n = 0 - n
型指定の部分は変わっていない。違うのは、関数抽象のバック...
引数を複数とる関数も同じように定義できる。次の示すのは、...
diffSquare :: Int -> Int -> Int
diffSquare a b = (a - b) * (a - b)
この構文を使った定義は、局所的・機械的な手順で通常の定義...
***where節 [#m0412c28]
上のdiffSquareの定義を再掲する。
diffSquare :: Int -> Int -> Int
diffSquare a b = (a - b) * (a - b)
この定義には「a - b」という式が二回出現している。これを一...
diffSquare :: Int -> Int -> Int
diffSquare a b = square (a - b)
square :: Int -> Int
square x = x * x
もうひとつ方法がある。''局所変数''を利用するものだ。同じ...
diffSquare :: Int -> Int -> Int
diffSquare a b = d * d
where
d = a - b
この定義の本体は、次の一行だ。
diffSquare a b = d * d
この式ではdという変数を使っている。dの定義は「where」以降...
whereは予約語で、where以降の部分を''where節''という。wher...
文法的には、where節は式ではなく定義に付属する。一つの定義...
where節中で関数定義の構文を使うこともできる。次の例は、引...
hasSmallFactor :: Int -> Bool
hasSmallFactor n = test 2 || test 3 || test 5
where
test k = mod n k == 0
「test k」は、nがkで割り切れるかどうかを返す。これはもち...
定義の右辺が複雑になるとき、式を分割して読み書きしやすく...
if式を使えば、ある年が閏年かどうか判定することができる。...
まず、ある整数が別の整数の倍数であるかどうかを判定する関...
divisible :: Int -> Int -> Bool
divisible n d = mod n d == 0
これを使って、西暦で表された年が閏年かどうかを判定する関...
isLeap :: Int -> Bool
isLeap n = divisible n 4 && (not (divisible n 100) || di...
これは複雑で、一見して何を表しているか分かりにくい。これ...
isLeap :: Int -> Bool
isLeap n = divisible n 4 && not exn -- n年が閏年なのは、...
where -- ただし
exn = divisible n 100 && not exn_of_exn -- 例外とは...
exn_of_exn = divisible n 400 -- 例外の例外とは、nが4...
***ガード [#b0888afb]
if式を使うと、条件に従って二つの選択肢から一つを選ぶ計算...
grade :: Int -> String
grade n =
if 80 <= n
then "優"
else if 70 <= n
then "良"
else if 60 <= n
then "可"
else "不可"
読み易いように複数行に分け、字下げを行なったが、それでも...
grade :: Int -> String
grade n
| 80 <= n = "優"
| 70 <= n = "良"
| 60 <= n = "可"
| True = "不可"
このように、定義の左辺の部分を書いた後、「| 条件 = 本体」...
これらの条件式の一つ一つを''ガード''と呼ぶ。
この例では、最後の条件が常にTrueなので、他の条件が全てFal...
-- 整数の絶対値を計算する
absolute :: Int -> Int
absolute n
| n < 0 = negate n
| otherwise = n
otherwiseは単なる変数で、「otherwise = True」と定義されて...
***ガードとwhere節 [#pec67af2]
ガードを使った定義にwhere節を付ける場合、where節は最後に...
-- 二次方程式 ax^2 + bx + c = 0 の解の個数を計算する
nRoot :: Int -> Int -> Int -> Int
nRoot a b c
| det > 0 = 2
| det == 0 = 1
| otherwise = 0
where
det = b * b - 4 * a * c
**続・コマンド [#m47719aa]
この節では、コマンドやdo式について、まだ説明していなかっ...
***do式の中でif式を使う [#n39b3e2b]
(TODO: 書く)
***return [#ab910692]
returnは「なにもしないで、結果だけ生成するコマンド」を作...
returnを使うと、自分で作ったコマンドがどんな値を結果とす...
foo :: IO Int
foo = do
getLine
return 1
もうすこし実用的な例として、ユーザにyes/noの質問をする関...
ask :: String -> IO Bool
ask question = do
putStrLn question
ln <- getLine
return (ln == "yes")
==演算子は、整数だけでなく様々な値の等しさを判定できる。...
こうして定義したaskは、例えば次のようにして使う。
main :: IO ()
main = do
a1 <- ask "Do you like cheese?"
a2 <- ask "Do you like cake?"
putStrLn
(if a1 && a2
then "Then you like cheesecake!"
else "I have nothing to say about you.")
***let文 [#qb80c1b8]
do式を書いていると、do式内部でのみ使える変数を定義したく...
main :: IO ()
main = do
input <- getLine
putStrLn (show (dig1 (read input)) ++ " + " ++ show (d...
-- 一の位
dig1 :: Int -> Int
dig1 x = mod x 10
-- 十の位
dig10 :: Int -> Int
dig10 x = dig1 (div x 10)
このプログラムを実行して「1234」と入力すれば、「4 + 3 = 7...
''let文''を使うと、このmainの定義を次のように書き直せる。
main :: IO ()
main = do
input <- getLine
let num = read input
let d1 = dig1 num
let d10 = dig10 num
putStrLn (show d1 ++ " + " ++ show d10 ++ " = " ++ sho...
このように、do式中に「let 変数 = 式」という形のものを書く...
なお、''文''とは、do式中に並べることができるもののことだ...
一つのlet文で複数の変数を定義することもできる。この場合、...
main :: IO ()
main = do
input <- getLine
let
num = read input
d1 = dig1 num
d10 = dig10 num
putStrLn (show d1 ++ " + " ++ show d10 ++ " = " ++ sho...
このdo式は三つの文から成り立っている。上から順に、束縛文...
**リスト [#wfdbae40]
プログラミングでは、データの「並び」をひとまとめにして扱...
***リストの表記 [#t2d7ee92]
リストは、式をコンマで区切り、全体を大括弧で括って書き表...
Prelude> [1, 2, 3, 2]
[1,2,3,2]
Prelude> [1 + 2, 3 + 4]
[3,7]
Prelude> [False, False, False, True]
[False,False,False,True]
リストの中身をそのリストの''要素''という。例えば2は[1,2]...
Prelude> [127]
[127]
Prelude> []
[]
あるリストについて、その要素の型は全て同じでないといけな...
**リスト操作の例/データ集計 [#i4a82046]
リストを操作するプログラムの例として、データ(たとえば気温...
入力データは、カレントディレクトリにinput.txtという名前で...
3
1
-1
-3
-2
この状態でプログラムを実行すると-1が表示される(平均は-0.4...
***平均を計算する関数 [#hbc242db]
このプログラムの肝は整数の列の平均を取るところにあるので...
整数のリストから平均を計算するには、要素の合計を要素数で...
Prelude> length [2,3,4]
3
Prelude> length []
0
また、要素の合計は関数sumで得られる。
Prelude> sum [2,3,4]
9
Prelude> sum []
0
だから、整数のリストからその平均値への関数averageは次のよ...
average :: [Int] -> Int
average xs = div (sum xs) (length xs)
試してみよう。
*Main> average [2, 5, 9]
5
*Main> average [-1, -2]
-2
*Main> average [99]
99
averageの引数が長さ零のリスト(''空リスト''という)なら、エ...
*Main> average []
*** Exception: divide by zero
***入出力を書く [#u30a2a1e]
テキストファイルの中身を読むには、readFileという関数を使...
readFile :: String -> IO String
readFileの引数としてファイル名を指定すると、そのファイル...
結果の表示にはputStrLnを使えばいいだろう。だから、mainの...
main :: IO ()
main = do
content <- readFile "input.txt"
putStrLn (process content)
ただし、processはこれから定義する関数で、ファイルの内容を...
既に見たように、ファイルの内容は"3\n1\n-1\n-3\n-2\n"のよ...
Prelude> lines "abc\ndef\n"
["abc", "def"]
Prelude> lines "stream and\npipelining"
["stream and", "pipelining"]
Prelude> lines "3\n1\n-1\n-3\n-2\n"
["3", "1", "-1", "-3", "-2"]
これで["3", "1", "-1", "-3", "-2"]のような文字列のリスト...
*Main> map show [1, 2, -3]
["1","2","-3"]
*Main> map length ["foo", "bar", "baz", "quux"]
[3,3,3,4]
*Main> map (\x -> x + 1) [1, 2, -3]
[2,3,-2]
*Main> map readInt ["3", "1", "-1", "-3", "-2"]
[3,1,-1,-3,-2]
以上を組み合せると、processは次のように定義できる。
process :: String -> String
process input = show (average (map read (lines input)))
**リスト操作の例/空行の削除 [#e0d19c75]
もう一つリスト操作をするプログラムの例をみておこう。input...
first line
second line
この場合、出力は次のようになってほしい。
first line
second line
***データの流れ [#cde4de32]
ファイルの内容を読み込んで、それをlinesで行単位に分割する...
Prelude> unlines ["ab", "c", "", "de"]
"ab\nc\n\nde\n"
unlinesはlinesのほぼ逆((元の文字列xが空でなく、しかも改行...
+linesで入力を行ごとに分割したリストを得る。入力文字列が"...
+この行のリストから、空文字列を除いたものを得る。この例な...
+これにunlinesを適用して、結果を得る。これは"first line\n...
mainは次のように定義できる。ただし、removeEmpties :: [Str...
main :: IO ()
main = do
input <- readFile "input.txt"
putStr (unlines (removeEmpties (lines input)))
putStrは、putStrLnと似ているが、文字列を出力するだけで、...
***filter [#z0f13cfa]
removeEmptiesを定義することを考えよう。このように、リスト...
Prelude> filter even [1, 2, 3, 4, 99]
[2,4]
Prelude> filter odd [1, 2, 3, 4, 99]
[1,3,99]
ここで、evenは「偶数である」という条件、oddは「奇数である...
evenやoddの正体は、単に「真偽値を返す関数」だ。
Prelude> even 2
True
Prelude> even 3
False
Prelude> even 4
True
このように、「偶数であればTrue、そうでなければFalseを返す...
Prelude> (\x -> x > 10) 5
False
Prelude> (\x -> x > 10) 10
False
Prelude> (\x -> x > 10) 12
True
Prelude> filter (\x -> x > 10) [9, 10, 12, 24]
[12,24]
removeEmptiesを定義する問題に戻ろう。removeEmptiesは、元...
removeEmpties :: [String] -> [String]
removeEmpties ls = filter (\x -> length x /= 0) ls
試してみる。
*Main> removeEmpties ["a", "^^", "", " ", "#########", ""]
["a","^^"," ","#########"]
**さらにリスト [#x482d57f]
この節では、リスト操作関数のうち、ここまでに紹介できなか...
***(++)とconcat [#da52391d]
++演算子は、二つのリストを連結したリストを返す。
Prelude> [True, False] ++ [False]
[True,False,False]
concatはリストのリストを受けとり、その要素を全て連結した...
Prelude> concat [[1, 2], [], [3], [4, 5, 6]]
[1,2,3,4,5,6]
Prelude> concat [[[1], []], [[2,3]]]
[[1],[],[2,3]]
***reverse [#sb2f2265]
reverse xはxを逆順にしたリストになる。
Prelude> reverse [1,2,3,4]
[4,3,2,1]
Prelude> reverse [[1,2], [3,4]]
[[3,4],[1,2]]
***replicate [#b8c18f63]
replicate n xはn個のxを要素とするリストを作る。
Prelude> replicate 5 7
[7,7,7,7,7]
Prelude> replicate 3 True
[True,True,True]
***数列表記 [#mc890872]
[1,2,3,4,5]のような連続する整数のリストは、専用の記法で生...
Prelude> [1..5]
[1,2,3,4,5]
Prelude> [2..2]
[2]
Prelude> [2..1]
[]
**文字 [#b4270166]
***文字のリスト [#w8e80007]
lengthや(++)といった関数が文字列にもリストにも使えること...
Haskellで文字を表すには、その文字を一重引用符で括ればよい...
文字の型はCharと書く。文字列は文字のリストだから、String...
同様に、例えば"abc"と['a', 'b', 'c']は同じ値を表している...
Prelude> ['f', 'u', 'l', 'l']
"full"
文字列は文字のリストなので、リスト操作関数の多くは文字列...
Prelude> concat ["foo", show (1+1), "bar"]
"foo2bar"
Prelude> reverse "Dog"
"goD"
***型シノニム [#g96968d6]
Stringは[Char]の別名だが、このような型に対する別名(''型シ...
type String = [Char]
次の例では、整数から真偽値への関数に「IntPred」という別名...
-- 整数から真偽値への関数、あるいは整数の「性質」
type IntPred = Int -> Bool
-- 二桁の整数であるという性質
doubleDigit :: IntPred
doubleDigit x = 10 <= x && x < 100
-- 二つの性質を両方満たすという性質
andp :: IntPred -> IntPred -> IntPred
andp p q = \x -> p x && q x
-- 1から100までの整数で、与えられた性質を満たすものがい...
count100 :: IntPred -> Int
count100 f = length (filter f [1..100])
main :: IO ()
main = do
putStrLn ("count100 doubleDigit = " ++ show (count100 ...
putStrLn ("count100 even = " ++ show (count100 even)) ...
putStrLn ("count100 (andp even doubleDigit) = " ++ sho...
**型 [#df8d45a5]
ここまで、型についてはいい加減な記述で逃げてたが、この節...
***静的な型付け [#qfcbaeb1]
全ての値には型がある。例えば"redone"の型はStringだし[True...
式の型を計算することを''型付け''という。型付けは失敗する...
Haskellでは、プログラムの実行が始まるより前に、プログラム...
これは、ghcコマンドでプログラムをコンパイルする場合が分か...
main :: IO ()
main = putStrLn (if not "鯖" then "2" else "1")
これを、例えばtypeerror.hsという名前を付けて保存し、ghcコ...
$ ghc --make typeerror.hs
[1 of 1] Compiling Main ( typeerror.hs, type...
typeerror.hs:2:24:
Couldn't match expected type `Bool' against inferred...
In the first argument of `not', namely `"\39894"'
In the predicate expression: not "\39894"
In the first argument of `putStrLn', namely
`(if not "\39894" then "2" else "1")'
このように、コンパイルの段階で型検査が行われ、エラーがあ...
一方、整数を0で割ろうとしたり、ガードを使った定義で条件を...
main :: IO ()
main = putStrLn (show (div 1 0))
これをghcコマンドでコンパイルすると、コンパイルは成功して...
***静的型付けによる制限 [#sbb19355]
Haskellでは、評価が始まる前に式が型検査されるので、式の評...
v :: Int
v = if 1 > 0 then 1 else "hell"
このif式の条件は必ず満たされるが、それは「1 > 0」を評価し...
この理由から、if式のthen部とelse部は同じ型の式でないとい...
***多相型 [#xe043929]
lengthの型について考えてみる。lengthは一引数の関数で、結...
Prelude> length [3, 3, 1]
3
Prelude> length [True, True, True]
3
Prelude> length [length, length]
2
GHCiには:type(省略形は:t)というコマンドがあって、式の型を...
Prelude> :t "ray"
"ray" :: [Char]
Prelude> :t 1 > 2
1 > 2 :: Bool
これを使ってlengthの型を調べてみると、以下のようになる。
Prelude> :t length
length :: [a] -> Int
「a」の部分は''型変数''といって、その部分を好きな型に置き...
型変数の名前はなんでもいい。例えば「length :: [b] -> Int...
型変数を含む型を''多相型''という。多相型を持つ値を''多相...
もう少し複雑な例を見てみよう。replicateの型は以下のとおり。
replicate :: Int -> a -> [a]
この型では型変数aが二回使われている。この場合もaは好きな...
複数の型変数を含む型もある。mapの型がその例だ。
map :: (a -> b) -> [a] -> [b]
例えば、map show [1, 2]という使い方では、aがIntに、bがStr...
***多相的な変数の定義 [#t4d025e5]
自分で多相的な変数を定義することができる。特別な記法は必...
mirror :: [a] -> [a]
mirror xs = xs ++ reverse xs
この関数は次のように使える。
*Main> mirror [1]
[1,1]
*Main> mirror "te"
"teet"
必要なら、より特殊な(一般的でない)型を指定することもでき...
mirror :: [Int] -> [Int]
***型推論 [#ndf5bdb9]
これまで、局所的でない定義(''最上位''の定義という)では、...
rjust :: Int -> String -> String -- ←これが型シグネチャ
rjust n s = spaces ++ s
where
-- ここにspacesの型シグネチャがない
spaces = replicate (n - length s) ' '
実際には、特殊な状況を除いて、型シグネチャは書いても書か...
最上位の変数については、推論の結果をGHCiから確認できる。...
mirror xs = xs ++ reverse xs
これをGHCiにロードし、:tコマンドで型を確認する。
*Main> :t mirror
mirror :: [a] -> [a]
正しい型が推論されている。
このように、型シグネチャを書くことは必須でないので、型シ...
-意図が明確になる。型の情報が書かれていると、その変数がど...
-エラーメッセージが分かり易くなる。変数の定義中で記述ミス...
この文書では、原則として、最上位の定義には型シグネチャを...
**型クラス [#v120721a]
***制約 [#r95dc2a7]
前に紹介したように、==演算子を使うと整数と整数を比較でき...
Prelude> 1 == 1
True
Prelude> 1 == 1 + 1
False
==演算子で比較できるのは実は整数だけではない。次のように...
Prelude> True == False
False
Prelude> '\10' == '\n'
True
Prelude> "tanasin" == "tanasinn"
False
Prelude> () == ()
True
Prelude> "f" == 'f'
<interactive>:1:7:
Couldn't match expected type `[Char]' against inferr...
In the second argument of `(==)', namely 'f'
In the expression: "f" == 'f'
In the definition of `it': it = "f" == 'f'
一方、比較できない型もある。関数やIOコマンドがその一例だ。
Prelude> length == length
<interactive>:1:0:
No instance for (Eq ([a] -> Int))
arising from a use of `==' at <interactive>:1:0-15
Possible fix: add an instance declaration for (Eq ([...
In the expression: length == length
In the definition of `it': it = length == length
Prelude> putStrLn "hello" == putStrLn "hello"
<interactive>:1:0:
No instance for (Eq (IO ()))
arising from a use of `==' at <interactive>:1:0-35
Possible fix: add an instance declaration for (Eq (I...
In the expression: putStrLn "hello" == putStrLn "hel...
In the definition of `it':
it = putStrLn "hello" == putStrLn "hello"
つまり、(==)は「a -> a -> Bool」という型を持つように振る...
(==)の型は次のように書く。
(==) :: Eq a => a -> a -> Bool
「Eq a」の部分が制約を表している。これは「型変数aは、Eqと...
「等値比較ができる」という性質を持つ型は具体的にはInt、Bo...
クラスの名前は大文字で始まる。
以下では、普段のプログラミングで頻繁に使うことになるだろ...
***Eq [#r5e4e8eb]
Eqは、(==)や(/=)で比較できる型を表す。Int、Bool、Charとい...
Eq制約を持つ関数の例としてelemがある。elemは、リストの中...
Prelude> :t elem
elem :: (Eq a) => a -> [a] -> Bool
Prelude> elem 3 [2,4,6]
False
Prelude> elem ' ' "second p"
True
***Ord [#xf10abbb]
Eqが「等しいかどうか」の判定を許す型のクラスであるのに対...
Int、Bool、CharはOrdのインスタンスだ。Charについては、Uni...
Eqの場合と同じく、リストは、要素の型がOrdのインスタンスで...
maxとminという関数は、二つの値から大きい方または小さい方...
Prelude> :t max
max :: (Ord a) => a -> a -> a
Prelude> max 4 5
5
Prelude> min "tree" "root"
"root"
一つのリストの要素のなかで最大のものと最小のものを得るに...
Prelude> :t maximum
maximum :: (Ord a) => [a] -> a
Prelude> maximum [10, 3, 7]
10
Prelude> minimum (map (\x -> x * (x - 7)) [1..100])
-12
Prelude> maximum []
*** Exception: Prelude.maximum: empty list
Ordの典型的な利用法として、リストを並べ換えるというのがあ...
ソースファイル中でsortを使うには、ファイルの先頭に次の一...
import List
これを''import宣言''という。これが必要なのは、sortがList...
GHCiでは、「import List」または「:module +List」と入力す...
Prelude> :t sort
<interactive>:1:0: Not in scope: `sort'
Prelude> import List
Prelude List> :t sort
sort :: (Ord a) => [a] -> [a]
これで、sort関数を試してみることができる。
Prelude List> sort [3,8,7,-1,99]
[-1,3,7,8,99]
Prelude List> sort ["az", "a", "ba", "bz", "b"]
["a","az","b","ba","bz"]
なお、lengthや(+)などの変数、IntやStringなどの型はimport...
***ShowとRead [#f7929d41]
showとreadも整数以外の多くの型に対して使える。対応するク...
show :: (Show a) => a -> String
read :: (Read a) => String -> a
Prelude> show 1
"1"
Prelude> show True
"True"
Prelude> show otherwise
"True"
Prelude> show [2, 3, 5]
"[2,3,5]"
Prelude> show 'c'
"'c'"
Prelude> show "string"
"\"string\""
実は、GHCiは(IOコマンド以外の)評価結果を表示する際にこのs...
Showクラスの利用例としてprint関数がある。printは、引数を...
print :: (Show a) => a -> IO ()
***文脈 [#q181a7cc]
一つの型シグネチャに複数の制約が必要なこともある。複数の...
makeList :: (Show a, Ord a) => [a] -> String
makeList xs = unlines (map show (sort xs))
このような制約の並び(この例では「(Show a, Ord a)」の部分)...
***クラス制約の曖昧さ [#i206d8ad]
多相的な変数のうち、型にクラス制約を含むもの(show、max、s...
このため、多重定義された変数については、その変数が具体的...
reshow :: String -> String
reshow x = show (read x)
この場合、readの結果の型とshowの引数の型が決まらないので...
このような場合、式の型を明示することで曖昧さを取り除くこ...
reshow :: String -> String
reshow x = show (read x :: Int)
式の型を明示するには「式 :: 型」という形の式を使う。定義...
なお、このように曖昧エラーになるのは、多重定義された変数...
decorate :: (Show a) => a -> String
decorate v = "<" ++ show v ++ ">"
この関数定義では、showの引数であるvの型がまだ確定していな...
**タプル [#ea360212]
***何それ [#hc7dcdae]
(TODO:タプルが欲しくなる例を書く)
''タプル''は複数の値をまとめたものだ。タプルは、値をコン...
Prelude> (1, True)
(1,True)
Prelude> (3 + 4, 3 < 4)
(7, False)
Prelude> ("z", True, "foo")
("z",True,"foo")
タプルの型は、要素の型をコンマで区切って、全体を括弧で囲...
***パターン照合 [#mc98f129]
タプルが与えられたとき、その要素の値を取り出すには''パタ...
v :: (Int, String)
v = (4, "Rich")
このように定義すると、vは(4, "Rich")に束縛される。
次に、これを以下のように書き換える。
a :: Int
b :: String
(a, b) = (4, "Rich")
こうすると、aは4に、bは"Rich"に束縛される。このように、定...
変数を束縛できる構文の多くでは、変数の代わりにパターンを...
sum3 :: (Int, Int, Int) -> Int
sum3 tup = x + y + z
where
(x, y, z) = tup
関数の仮引数としてパターンを直接書いてもいい。sum3は次の...
sum3 :: (Int, Int, Int) -> Int
sum3 (x, y, z) = x + y + z
***fstとsnd [#f9ab5b55]
このように、タプルの内容はパターン照合で取り出すのが普通...
fst :: (a, b) -> a
snd :: (a, b) -> b
fstは対の第一要素、sndは第二要素を返す。
Prelude> fst (True, 44)
True
Prelude> snd (True, 44)
44
*リンク [#d251be92]
**公式 [#s71afa0c]
-http://www.haskell.org/
**処理系 [#je126b42]
:GHC|http://www.haskell.org/ghc/
:Hugs|http://www.haskell.org/hugs/
**その他 [#e7d4d60c]
:[[The Haskell 98 Report (Revised):http://haskell.org/onl...
:[[Foreign Function Interface:http://www.cse.unsw.edu.au/...
:[[Hierarchical modules:http://www.haskell.org/hierarchic...
:[[The Glorious Glasgow Haskell Compilation System User's...
:[[Haskell Hierarchical Libraries:http://www.haskell.org/...
:[[HackageDB:http://hackage.haskell.org/packages/hackage....
:[[Hoogle:http://www.haskell.org/hoogle/]]|ライブラリ関数...
**日本語サイト [#cd41828e]
:[[Programming in Haskell:http://www.sampou.org/cgi-bin/h...
:[[Haskell 98 言語とライブラリ 改訂レポート:http://www.sa...
:[[栄光のグラスゴーHaskellコンパイルシステム利用の手引き:...
ページ名: