top page > computer > haskell > web_lecture > for_programmer > list_comprehensions2.html
更新日:
文責: 重城良国

構文: リスト内包表記2

はじめに

リスト内包表記によるより複雑な表現を学ぶ。関数concatMapを使った表現に脱糖される。

mapの入れ子

リスト[1, 2, 3]と[4, 5]のすべての組み合わせでかけ算する。関数mapを入れ子にする。

% ghci
Prelude> map (\x -> map (\y -> x * y) [4, 5]) [1, 2, 3]
[[4,5],[8,10],[12,15]]

「すべての組み合わせ」なのでリストのリストよりも単なる数のリストのほうが良い。

Prelude> concat $ map (\x -> map (\y -> x * y) [4, 5]) [1, 2, 3]
[4,5,8,10,12,15]

concatMapの連鎖

concatMapを使うと

% ghci
Prelude> concatMap (\x -> map (\y -> x * y) [4, 5]) [1, 2, 3]
[4,5,8,10,12,15]

となる。今度は[1, 2, 3]と[4, 5]と[6, 7]のすべての組み合わせでかけ算をする。concatMapを入れ子にする。

Prelude> concatMap (\x -> concatMap (\y -> map (\z -> x * y * z) [6, 7]) [4, 5]) [1, 2, 3]
[24,28,30,35,48,60,70,72,84,90,105]

\x -> concatMap (\y -> map (\z -> x * y * z) [6, 7]) [4, 5]の部分を考える。[4, 5]と[6, 7]のすべての組み合わせのかけ算にxをかけたものを数値のリストとして返す。全体は変数xを1, 2, 3でそれぞれ束縛してこの動作を行い、結果のリストをconcatする。

3重、4重、...と入れ子にしていく。読みやすさのために書きかたを変える。:{と:}は対話環境で複数行に分けるための括弧だ。

Prelude> :{
Prelude| (`concatMap` [1, 2, 3]) $ \x ->
Prelude| (`concatMap` [4, 5]) $ \y ->
Prelude| (`map` [6, 7]) $ \z ->
Prelude| x * y * z
Prelude| :}
[24,28,30,35,48,60,70,72,84,90,105]

すこし工夫すると最後のmapをconcatMapにそろえられる。

Prelude> :{
Prelude| (`concatMap` [1, 2, 3]) $ \x ->
Prelude| (`concatMap` [4, 5]) $ \y ->
Prelude| (`concatMap` [6, 7]) $ \z ->
Prelude| [x * y * z]
Prelude| :}
[24,28,30,35,48,60,70,72,84,90,105]

入れ子というよりも連鎖のようになる。

リスト内包表記

「すべての組み合わせで...する」ことはよくあるので構文糖がある。concatMapの連鎖を

Prelude> [ x * y * z | x <- [1, 2, 3], y <- [4, 5], z <- [6, 7] ]
[24,28,30,35,48,60,70,72,84,90,105]

のように書ける。

ろ過

Prelude> [ x | x <- [1, 2, 3, 4, 5], y <- [8, 123] ]
[1,1,2,2,3,3,4,4,5,5]
Prelude> [ x | x <- [1, 2, 3, 4, 5], y <- [123] ]
[1,2,3,4,5]
Prelude> [ x | x <- [1, 2, 3, 4, 5], y <- [] ]
[]

xを[1, 2, 3, 4, 5]の要素で次々に束縛し[]とのすべての組み合わせについてxを返す。何もないものとの組み合わせると結果として何もなくなる。これを利用してろ過ができる。

Prelude> [ x | x <- [1, 2, 3, 4, 5], y <- if even x then [1234] else [] ]
[2,4]

[1, 2, 3, 4, 5]のうち偶数は[1234]との組み合わせを奇数では[]との組み合わせを返す。偶数だけをろ過したことになる。[1234]は[111]でも['a']でも[False]でも何でも良い。要素数1のリストならば結果は変わらない。

ユニット型

Haskellの型で最も単純な型はユニット型だ。Bool型にはTrueとFalseのふたつの値がある。ユニット型にはひとつの値しかない。ユニット型は()であり値も()と表記される。

Prelude> let x = ()
Prelude> x
()
Prelude> :t x
x :: ()

ユニット型は0要素タプルだ。単なる値は1要素タプルだ。3要素タプルからユニット型まで順に並べてみる。

(x, y, z), (x, y), (x), ()

ユニット型が0要素タプルであることがわかる。

ユニット型でろ過

Prelude> [ x | x <- [1, 2, 3, 4, 5], _ <- if even x then [()] else [] ]
[2,4]

ユニット型を使えば意味のない値を書かずにすむ。値に意味がない(情報量が0)ことを明示できる。

もっと構文糖

もうすこし甘くする。さらに砂糖をまぶす。if式の条件部分だけを直接置く。

Prelude> [ x | x <- [1, 2, 3, 4, 5], even x ]
[2,4]

まとめ

concatMapを連鎖させて「すべての組み合わせ」の演算ができる。このパターンは頻出で構文糖がある。「リスト内包表記」だ。さらにろ過のための構文糖もある。

課題

  1. 1から5の整数で三角形をつくれる3つ組をすべて求めよ
  2. 上記で順番をいれかえたものを同じと考える版を作成せよ

「構文: リスト内包表記1」へもどる 「パーサ: はじめに」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics