ようこそ。睡眠不足なプログラマのチラ裏です。

Haskellのお勉強 その8

基本的な値と、その周辺の関数

真偽値
Haskellno真偽値はTrueかFalseのみ。その型はBool型。
関連関数として、not関数、(&&)関数、(||)関数などがある。

Prelude> not True
False
Prelude> (&&) True False
False
Prelude> not True && False
False
Prelude> False || True
True

まあ、基本ですね。


数値
Haskellの整数型には、IntとInteger型がある。Intは範囲の小さな符号付き整数値*1
Integer型は、範囲無制限の整数値を表現することができる。


確認のために、「::」構文を使って型を明示的に宣言してみると以下のようになる。

Prelude> 999999999999999999999999999999999999999 :: Integer
999999999999999999999999999999999999999
Prelude> 999999999999999999999999999999999999999 :: Int
-1


整数は、10進法、8進法、16進法の3つのリテラルで表現することができる。
「0o」(ゼロ・オー)を前置すると8進数を表し、「0x」(ゼロ・エックス)を前置すると16進数を表す。

Prelude> 0o644
420
Prelude> 0x1f
31

これもまあ、基本ですね。


Haskell浮動小数点型には、32ビット表現(単精度)であるFloat型と、
64ビット表現(倍精度)である、Double型がある。

以下に浮動小数リテラルの例をいくつか示す。

Prelude> 3.14159265358979323
3.1415927
Prelude> 3.14159265358979323
3.141592653589793
Prelude> 1500e+3
1500000.0
Prelude> 1500e-3
1.5


以下で示す他にも、いろいろな数値演算の関数がある。

Prelude> div 33 5
6
Prelude> mod 33 5
3
Prelude> -55
-55
Prelude> negate 55
-55
Prelude> -(55 -50)
-5
Prelude> abs $ -50
50
Prelude> floor 3.141592
3
Prelude> ceiling 3.141592
4

Haskellでは、整数型と浮動小数点型の他に、有理数を表現する
Rational型と、複素数を表現するComplex型がある。
Rational型はRatioモジュールを、Complex型はComplexモジュールを
それぞれインポートすると使えるようになる。

有理数: http://www.sampou.org/haskell/report-revised-j/ratio.html
複素数: http://www.sampou.org/haskell/report-revised-j/complex.html


文字と文字列
Haskellの文字はChar型の値。文字コードUnicodeが使われているが、
GHCでは文字自体はUnicodeで表現されているが、
入出力での文字エンコーディングの変換がまだ実装されていない。

Prelude> Char.isUpper 'a'
False
Prelude> Char.isAlpha 'a'
True
Prelude> Char.isUpper 'A'
True
Prelude> Char.isHexDigit 'A'
True
Prelude> Char.isDigit 'A'
False
Prelude> Char.chr 97
'a'
Prelude> Char.ord 'a'
97
Prelude> map Char.ord "aiueo"
[97,105,117,101,111]
Prelude> Char.toLower "A"
'a'

以下、主張なエスケープシーケンス

記述例 意味
\t タブ
\n 改行
\r 復帰
\v 垂直タブ
\f 改ページ
\a ベル
\b バックスペース
\NNN 10進表記の数値NNNに対応する文字
\oNN 8進表記の数値NNに対応する文字
\xNN 16進表記のNNに対応する文字
\^X コントロールX
\' シングルクオート
\" ダブルクオート
\\ バックスラッシュ


タプル
Haskellにおけるタプルとは、いくつかの値 (数値型、文字列型など)をひとつにまとめて、
あたかもひとつの値のように扱う機能である。
リストや配列が同じ型をまとめるのに対し、タプルは用途や型が異なる型をひとつにまとめるために使われる。
このためタプルの働きは、他言語で言うところの構造体に似ていると言える。
構造体には通常異なる型や名前をもった値がまとめて格納される。
これによって、本来ならばひとつの値しか扱えない箇所 (関数の返り値や、変数への代入、配列の各要素など) で
複数の値を同時に扱うことが可能となる。タプルはこの構造体を匿名にしたようなものと考えても差し支えないだろう。


なお、タプルは基本的に2つの要素以上でなければならず、1つの要素のみのタプルというのはない。
しかし、特別に0要素のタプルはある。0要素のタプルはユニットと呼ばれている。
ユニット(unit)は0要素のタプルである。ソースコード上では「()」と記述され、
また、ユニットの型はリテラルと同じく「()」である。

Prelude> (4,"aiueo") :: (Int,String)
(4,"aiueo")
Prelude> ("test",45) :: (String,Int)
("test",45)
Prelude> (1,"yes",[3,2,1]) :: (Int,String,[Int])
(1,"yes",[3,2,1])
Prelude> ((3,5),"ok",['a','b','c']) :: ((Int,Int),String,[Char])
((3,5),"ok",['a','b','c'])
Prelude> fst ('a',6)
'a'
Prelude> snd $ snd ("aiueo",(6,"test"))
"test"
Prelude> zip [1, 2, 3, 4, 5] [9, 8, 7]
[(1,9),(2,8),(3,7)]
Prelude> unzip $ zip [1, 2, 3, 4, 5] [9, 8, 7]
([1,2,3],[9,8,7])


リスト
Haskellにおけるリストとは、同じ型の値を並べたもの。
Haskellのリストは所謂、一方向リンクリストと同質で、
前方からリストを辿ることは可能だが、後方からリストを辿ることはできない。

Prelude> 1 : 2 : 3 : []
[1,2,3]
Prelude> let cons = (:)
Prelude> (cons 1 (cons 2 (cons 3 (cons 4 []))))
[1,2,3,4]
Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> [2,4..12]
[2,4,6,8,10,12]
Prelude> null []
True
Prelude> null[1,2,3]
False
Prelude> 1 : 2 : 3 : [4,5] ++ []
[1,2,3,4,5]


リスト内包表記
Haskellにはmap関数やfillter関数を組み合わせた関数を数学的に表現する
リスト内包表記(list comprehension)という構文がある。

Prelude> [abs x | x <- [-5..5]]
[5,4,3,2,1,0,1,2,3,4,5]

上記のリスト内包表記は、「リスト[-5..5]の各要素に対して(abs x)を集める」と読む。
map関数を使って書くと、 map abs [-5..5]となる。


リスト内包表記を使ったものとして、よくクイックソートを実装した
以下のqsort関数が例としてあげられる。

main = putStr =<< return . unlines . qsort . lines =<< getContents
 
qsort :: (Ord a) => [a] -> [a]
qsort []     = []
qsort (x:xs) = qsort lt ++ [x] ++ qsort gteq
                 where
                   lt   = [y | y <- xs, y < x]
                   gteq = [y | y <- xs, y >= x]


また、下記の例では、2つのリストの要素の組み合わせが生成される。

Prelude> [(x,y) | x <- ['a','b','c'], y <- [1,2,3]]
[('a',1),('a',2),('a',3),('b',1),('b',2),('b',3),('c',1),('c',2),('c',3)]


以下、九九の結果リスト

main = print $ multiplication [1..9]

multiplication :: [Int] -> [Int]
multiplication zs = [x * y | x <- zs, y <- zs] 

*1:具体的な範囲は処理系によって異なる。32ビットマシン上のGHCであれば32ビットの符号付き整数値となる