Posted at

すごいH本読んだからまとめ 2 「リスト,タプル」


はじめに


Haskellのリストの性質


  • 表記は[1,2,3,4,5]のように書く。

*Main Lib> [1,2,3,4,5]

[1,2,3,4,5]


  • 同じ型の要素のみリストにいれることができる。

*Main Lib> [1,2,'a'] -- 別の要素入れるとエラー

<interactive>:21:2: error:
? No instance for (Num Char) arising from the literal 1
? In the expression: 1
In the expression: [1, 2, 'a']
In an equation for it: it = [1, 2, 'a']


  • Haskellにおける文字列は文字のリストとして考えることができる

*Main Lib> ['a','i','u','e','o'] == "aiueo" -- 左右は同じもの

True


リストを操作する


リストの連結

--「++」リスト同士の連結

*Main Lib> [1,2,3] ++ [4,5,6]
[1,2,3,4,5,6]

-- 「:」要素とリストの連結
*Main Lib> 5:[1,2,3,4] -- 先頭に5を追加
[5,1,2,3,4]

*Main Lib> [1,2,3,4,5]:8 -- 逆からくっつけようとするとエラー
<interactive>:20:1: error:
? Non type-variable argument in the constraint: Num [[a]]
(Use FlexibleContexts to permit this)
? When checking the inferred type
it :: forall a. (Num a, Num [[a]]) => [[a]]


リストの比較


  • 内部の要素を前から比べていく。

*Main Lib> [1,2,3] < [1,2,4] -- リストの始めから比較して、最後の要素で大小判定した。

True
*Main Lib> [1,100,100] < [2,0,0] -- 先頭の要素の大小で全部決まる。
True
*Main Lib> "aaa" < "bbb" -- aよりbのほうが大きい
True
*Main Lib> [1,2,3,4,5] < [2] -- 要素数が違くても比べられる。
True
*Main Lib> "aaa" < "b"
True


リストの要素取得

--  「!!」n番目のリストを取得

*Main Lib> [1,2,3,4,5] !! 4
5 -- オフセットは0

*Main Lib> head [1,2,3,4,5] -- 先頭要素
1
*Main Lib> tail [1,2,3,4,5] -- 先頭要素以外のリスト
[2,3,4,5]
*Main Lib> last [1,2,3,4,5] -- 最終要素
5
*Main Lib> init [1,2,3,4,5] -- 最終要素以外のリスト
[1,2,3,4]

*Main Lib> reverse [1,2,3,4,5] -- 逆順で取得
[5,4,3,2,1]
*Main Lib> take 3 [1,2,3,4,5] -- 最初から3要素取得
[1,2,3]
*Main Lib> drop 3 [1,2,3,4,5] -- takeしたぶん以外を取得
[4,5]

-- Haskellの関数を変数に適用しても元々の変数を変更しない。(そもそも変数じゃなくて定数だけど)
*Main Lib> let myList = [1,2,3,4,5]
*Main Lib> drop 3 myList
[4,5]
*Main Lib> myList
[1,2,3,4,5]

他にもいろいろ使える関数がある。


レンジ


  • 連続するリストの省略記法

  • 2番目の値を書くと、連続するステップを調節できる。

-- 1から100までのリストの書き方

*Main Lib> [1 .. 100]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]

-- 1から100まで、1つ飛ばしリストの書き方
*Main Lib> [1,3 .. 100]
[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99]

-- 100から1までのリストの書き方
*Main Lib> [100, 99 .. 1]
[100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
*Main Lib> [100 .. 1] 失敗例
[]


  • 最後の値を省略すると無限リストを書ける。

  • 無限リストを関数の引数として渡すと、必要な分だけ評価される。(遅延評価)

*Main Lib> take 5 [1,10 ..]

[1,10,19,28,37]
*Main Lib> take 5 [0,10 ..]
[0,10,20,30,40]
*Main Lib> take (cycle [1,2,3])
<interactive>:40:7: error:
? Couldn't match expected type Int with actual type [Integer]
? In the first argument of take, namely (cycle [1, 2, 3])
In the expression: take (cycle [1, 2, 3])
In an equation for it: it = take (cycle [1, 2, 3])


無限リストを作れる関数達

*Main Lib> take 7 (cycle [1,2,3])

[1,2,3,1,2,3,1]
*Main Lib> take 7 $ repeat 10
[10,10,10,10,10,10,10]
*Main Lib> replicate 3 10
[10,10,10]
*Main Lib> replicate 7 10
[10,10,10,10,10,10,10]


リスト内包表記


基本の書き方

*Main Lib> [x + 3 | x <- [1..10], x `mod` 2 == 0 ]

[5,7,9,11,13]

[出力関数 | 要素 <-[リスト], フィルター]


  1. まずはリストから各要素を取り出す。

  2. 要素をフィルターに掛けてTrueのものだけ取り出す。

  3. 取り出された各要素に出力関数を適用したものを新たなリストの要素とする。

リストから要素の取り出しと、フィルターはカンマで区切って複数書ける。


複数のリストを使用した内包表記

要素を取りだすリストが複数ある時の動作を確認する。予想外の動きをした。

*Main Lib>  [(x,y) | x <- [1..3], y <- [1..3]]  --予想は[(1,1), (2,2), (3,3)]

[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)] -- 順番ごとの組み合わせではなく全組み合わせで出てくる。

-- filterをかけてみる
*Main Lib> [x+y | x <- [1..3], y <- [1..3], x `mod` 2 == 0 ]
[3,4,5]
*Main Lib> [(x,y) | x <- [1..3], y <- [1..3], x `mod` 2 == 0 ]
[(2,1),(2,2),(2,3)] -- xが偶数のものだけ出力
*Main Lib> [(x,y) | x <- [1..3], y <- [1..3], x `mod` 2 == 0 , y `mod` 2 == 0] --フィルター追加
[(2,2)]


タプル


構造

(1,"3")

(2, 5, 3, 4)
("わたし", "あなた")
("わたし", True, False)


性質


  • 同じ型として扱うために、要素数は等しくなければならない

  • 同じ型として扱うために、要素の型のパターンは等しくなければならない

*Main Lib> ("あなた", "わたし") == ("あなた", "わたし")

True
*Main Lib> ("あなた", "わたし") == ("あなた", "ほかのひと")
False
-- 要素数が違う
*Main Lib> ("あなた", "わたし") == ("あなた", "わたし", "わたし")

<interactive>:4:19: error:
? Couldn't match expected type ([Char], [Char])
with actual type ([Char], [Char], [Char])
? In the second argument of (==), namely ("あなた", "わたし", "わたし")
In the expression: ("あなた", "わたし") == ("あなた", "わたし", "わたし")
In an equation for it:
it = ("あなた", "わたし") == ("あなた", "わたし", "わたし")
-- 型のパターンが違う。
*Main Lib> ("あなた", "わたし") == ("あなた", 1)
<interactive>:3:27: error:
? No instance for (Num [Char]) arising from the literal 1
? In the expression: 1
In the second argument of (==), namely ("あなた", 1)
In the expression: ("あなた", "わたし") == ("あなた", 1)


タプルが2要素の場合の値取得

-- 1個目

*Main Lib> fst (8, 11)
8
-- 2個目
*Main Lib> snd (8, 11)
11


zip関数

2つのリストをタプルにまとめる。

Haskell

*Main Lib> zip [1,2,3] [1,2,3]

[(1,1),(2,2),(3,3)]