« ”すごいHaskellたのしく学ぼう!”でHaskellを始めてみようと思います | トップページ | タプルとはなんだろうか? - Haskell »

2018-07-08

関数呼び出し、リスト、内包表記 -- Haskell

すごいHaskellたのしく学ぼう!ですが、まず、第一章の中盤まで。 関数呼び出しとリストについて勉強しました。

関数呼び出し

Lisp系などでは、演算子も

(+ 1 1)

のように左に置く(前置)ので、慣れるまでちょっと違和感を感じる事もありますが、Haskellでは演算子は中置できるそうです。

1 + 1
ややっこしいから統一して欲しいという時には、以下のように前置できる。

(+) 1 1

リスト

連結

リスト同士の連結は ++ 演算子で行う。これは中置できる。 で、文字列は文字のリストで表現されているため、文字列の結合も ++ 演算子で行う。

[1, 1, 1] ++ [2, 2, 2] ++ [3, 3, 3] -- [1, 1, 1, 2, 2, 2, 3, 3, 3]
"Hello " ++ "Haskell" -- "Hello Haskell"

C言語などのプログラミングでは ++ はインクリメントの単項演算子ですが、まぁソースの前後を見れば何をやっているのか 何となくはわかりますね。

ランダムアクセス

C言語の配列に添字でアクセスするようにリストの要素に先頭からの位置でアクセスが可能です。

[1, 2, 3, 4, 5] !! 2 -- 3 添字は0から始まる
[[10, 11, 12], [20, 21, 22], [30, 31, 32, 33]] !! 1 -- [20, 21, 22]
[[10, 11, 12], [20, 21, 22], [30, 31, 32, 33]] !! 1 !! 2 -- 22

と、この辺までは、なんとなく理解できました。書き方は違っていますが、考え方は他のプログラミング言語にもよく出てきます。 ここからちょっとややこしくなりました。

比較

まず、リストのまま比較できます。まぁ、これも考え方を少し変えてみれば不思議ではありません。文字列を比較演算子で 比較できる言語は結構あると思います。C言語はstrcmpで0との比較になりますね。

[1, 2, 3] < [1, 2, 4] -- True
[3, 2, 1] == [3, 2, 1] -- True
[2, 4, 6] /= [2, 4, 6] -- False
リスト関係の基本的な関数
head
先頭の要素を返す
tail
先頭を取り除いた残りの要素を返す
last
最後の要素を返す
init
最後を取り除いた残りの要素を返す
length
リストの長さを返す
null
空リストかどうかを論理値で返す
reverse
リストを逆順にする
take
先頭から指定数の要素をリストとして返す
take 3 [1, 2, 3, 4, 5] -- [1, 2, 3]
drop
先頭からして指数の要素を削除して残りの要素をリストとして返す
drop 2 [1, 2, 3, 4, 5] -- [3, 4, 5]
maximum
リスト中の最大の要素を返す
minimum
リスト中の最小の要素を返す
sum
リストの要素を全て足した値(和)を返す
product
リストの要素を全て掛けた値(積)を返す
elem
リストの中に要素が含まれているかを論理値で返す
中置関数で書かれる事が多いそうです。
elem 3 [1, 2, 3, 4, 5] -- True
8 `elem` [1, 2, 3, 4, 5] -- False
cycle
リストを永遠に繰り返す無限リストを返す
take 5 (cycle [1, 2, 3]) -- [1, 2, 3, 1, 2]
repeat
ひとつの要素を無限に繰り返すリストを返す
take 5 (repeat 'Z') -- "ZZZZZ"
putStrLn (take 4 (repeat 'あ')) -- 「ああああ」と出力する
replicate
ひとつの要素を指定回数繰り返すリストを返す
putStrLn (replicate 4 'あ') -- 「ああああ」と出力
replicate 5 7 -- [7, 7, 7, 7, 7]
範囲でリストを作成

例えば1から1000までのリストを作りたい時に[1, 2, 3, 4, 5, ......... 1000]って 書くのは大変ですが、リストを範囲で指定できます。

[1..1000] -- 1から1000までのリスト
[10, 20..1000] -- 10から10ずつ増加で1000まで [10, 20, 30, 40 ....]
[10, 20..] -- 10から10ずつ増加の無限リストtakeなどと組み合わせて使うと便利

pythonでは

range(1, 10) # 1, 2, 3, 4, 5, 6, 7, 8, 9
range(10, 1001, 10) # 10から10ずつ増加で1000まで [10, 20, 30, 40 ....]

と、第二引数より小さい値でリストを作成しますが、Haskellでは書いた値を含む リストを作るのでpythonプログラマーは注意が必要ですね。

リストの内包的表記

1000までのリストも簡単に作れる事がわかりました。今度は1000までの3か5の倍数をリストにしたいとします。

[3, 5, 6, 9, 10, 12, 15, 18 ...... ]

ってやらなくちゃならなくなります。 数学では、これを集合の内包的記法という表現方法で表します。(らしいです)

{x|1 <= x <= 1000 かつ xは3か5の倍数}

みたいな感じらしいです。

たしかにこうすれば、ややこしいリストが必要な場合でもわりと簡単に作成する事ができますね。 Haskellではこれを再現できるようです。

[x | x <- [1..1000], x `mod` 3 == 0 || x `mod` 5 == 0]

と表現します。

ちなみにpythonでは

[x for x in range(1, 1001) if x % 3 == 0 or x % 5 == 0]

と書けます。

で、この3と5の倍数の各要素を7倍したリストというのも作れます。

[x * 7 | x <- [1..1000], x `mod` 3 == 0 || x `mod` 5 == 0]

となります。

pythonでも同じように

[x * 7 for x in range(1, 1001) if x % 3 == 0 or x % 5 == 0]

とすれば同じリストを作成する事ができます。

本体価格のリストから消費税8%込のリストを作ってみました。

[round (price * 1.08) | price <- [1000, 1200, 900, 560, 480]]

次回、タプルを勉強してみたいと思います。

|

« ”すごいHaskellたのしく学ぼう!”でHaskellを始めてみようと思います | トップページ | タプルとはなんだろうか? - Haskell »

Haskell」カテゴリの記事

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: 関数呼び出し、リスト、内包表記 -- Haskell:

« ”すごいHaskellたのしく学ぼう!”でHaskellを始めてみようと思います | トップページ | タプルとはなんだろうか? - Haskell »