Haskell 勉強メモ9

セクション

中置関数に対して、セクションという機能を使って部分適用をすることができる。

divideByTen :: (Floating a) => a -> a
divideByTen = (/10)

*Main> divideByTen 30
3.0

これは 30 / 10 と同義。

高階関数

関数を受け取り、2回適用する関数を書いてみる。先にScalaで書いてみよう。

def applyTwice[T](f: T => T)(x: T): T = f(f(x))

applyTwice { v: Int => v * 2 }(10) // 40

これを Haskell で書くとこうなる。

applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)

*Main> applyTwice (\a -> a * 2) 10
40

`(\a -> a * 2)` は Haskell の無名関数

zipWith

標準ライブラリの zipWith

まずはHaskell標準の zipWith の挙動を確認する。

*Main> :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

zipWith は 2つの引数をとって1つの値を返す関数と、2つのリストを引数に取り、2つのリストの各要素に関数を適用することで、1つに結合する。

*Main> zipWith (\a b -> a + b) [1,2,3] [4,5,6]
[5,7,9]

Scalaには zipWith に相当するものはないが、zip メソッドが collection 系には実装されている

List(1, 2, 3) zip List(2, 3, 4) // res2: List[(Int, Int)] = List((1,2), (2,3), (3,4))

2つのリストを引数にとって、前から順に同じ位置にある要素で構成されたタプルのリストを作って返す。これに map を適用してあげれば、先程の haskell の zipWith 相当のことが可能だ。

List(1, 2, 3) zip List(2, 3, 4) map { case (a, b) => a + b } // res2: List[Int] = List(3, 5, 7)

zipWith を実装してみる。※ 途中で、gist を使えば haskell もsyntax highlight いけることに気づいたので少し行数があるやつは試しに。

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x : xs) (y : ys) = f x y : zipWith' f xs ys
view raw zipWith.hs hosted with ❤ by GitHub

Scalaで同じようなことを書くとこんな感じ。最初の zipmap を連携させる方が読みやすいし書きやすい。

def zipWith[A, B, C](f: (A, B) => C)(listA: List[A])(listB: List[B]): List[C] = {
(listA, listB) match {
case (Nil, _) => Nil
case (_, Nil) => Nil
case (a :: as, b :: bs) => f(a, b) +: zipWith(f)(as)(bs)
}
}
zipWith { (a: Int, b: Int) => a + b }(List(1,2,3))(List(4,5,6))
view raw zipWith.scala hosted with ❤ by GitHub

flip

普段Scalaを使っていて、さっきの zipWith なんかは似たようなもの、似たようなことを Scala でもやったことがあるが、これはhaskell本で初めてみる。

flip は関数を引数にとり、引数が入れ替わった関数を返す。※正直、何が嬉しいのか全く分からない。

Main> :t flip
flip :: (a -> b -> c) -> b -> a -> c
*Main> zip [1,2,3,4,5] "hello"
[(1,'h'),(2,'e'),(3,'l'),(4,'l'),(5,'o')]
*Main> flip zip [1,2,3,4,5] "hello"
[('h',1),('e',2),('l',3),('l',4),('o',5)]

zip は1つ目の配列と2つ目の配列の同じ位置にある要素で作ったタプルのリストを返す関数だ。1つ目の通常の zip では左の配列にあったものがタプルの左に、右の配列にあったものがタプルの右側にあるのが分かる。

flip を適用した zip はそれが逆になっている。:t で見てみるとよく分かるが、引数が入れ替わっていることが分かる。

*Main> :t zip
zip :: [a] -> [b] -> [(a, b)]
*Main> :t flip zip
flip zip :: [b] -> [a] -> [(a, b)]

自分で実装すると下記のようになる。

flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f = g
where g x y = f y x
view raw flip.hs hosted with ❤ by GitHub

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。