はじめに
- 前回
- Elixirを使いはじめてだいたい1年くらいがたちました
- すごいHaskellたのしく学ぼう!という本を2015年に買って、一通り読んだあとずっと本棚にしまわれていたままでした
- 久しぶりに引っ張り出して読んでみると、こんなに愉快な内容だったけ!? という感想を持ちました
- Elixirで関数プログラミングにだいぶ慣れたので、ユーモアの部分を楽しむ余裕ができたのだとおもいます
- 少しずつ読み進めながら、興味が向いたところだけElixirで書き換えてみたりして理解を深めていきたいとおもいます
- 今回は第5章 高階関数
applyTwice
Haskell
Prelude> applyTwice f x = f (f x)
Prelude> applyTwice (+3) 10
16
Elixir
awesome.exs
defmodule Awesome do
def apply_twice(fun, x), do: fun.(x) |> fun.()
end
iex> Awesome.apply_twice(&(&1 + 3), 10)
16
zipWith
Haskell
baby.hs
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
Prelude> :l baby.hs
*Main> zipWith' (+) [4,2,5,6] [2,6,2,3]
[6,8,7,9]
*Main> zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]
["foo fighters","bar hoppers","baz aldrin"]
Elixir
awesome.exs
defmodule Awesome do
def zip_with(_, [], _), do: []
def zip_with(_, _, []), do: []
def zip_with(fun, [x | xs], [y | ys]), do: [fun.(x, y) | zip_with(fun, xs, ys)]
end
iex> Awesome.zip_with(&(&1 + &2), [4,2,5,6], [2,6,2,3])
[6, 8, 7, 9]
iex> Awesome.zip_with(&(&1 <> &2), ["foo ", "bar ", "baz "], ["fighters", "hoppers", "aldrin"])
["foo fighters", "bar hoppers", "baz aldrin"]
map
Haskell
Prelude> map (+3) [1,5,3,1,6]
[4,8,6,4,9]
Elixir
iex(47)> [1,5,3,1,6] |> Enum.map(&(&1 + 3))
[4, 8, 6, 4, 9]
filter
Haskell
Prelude> filter (>3) [1,5,3,2,1,6,4,3,2,1]
[5,6,4]
Elixir
iex> Enum.filter([1,5,3,2,1,6,4,3,2,1], &(&1 > 3))
[5, 6, 4]
10万以下の数のうち3829で割り切れる最大の数を探してみる
Haskell
baby.hs
largestDivisible = head (filter p [100000, 99999..])
where p x = x `mod` 3829 == 0
Prelude> :l baby.hs
*Main> largestDivisible
99554
Elixir
iex> (
...> Stream.iterate(100000, &(&1 - 1))
...> |> Stream.filter(&(rem(&1, 3829) == 0))
...> |> Enum.take(1)
...> |> hd
...> )
99554
1から100までの数のうち、長さ15以上のコラッツ列の開始数になるものはいくつあるか?
Haskell
baby.hs
chain 1 = [1]
chain n
| even n = n : chain (n `div` 2)
| odd n = n : chain (n * 3 + 1)
numLongChians = length (filter isLong (map chain [1..100]))
where isLong xs = length xs > 15
Prelude> :l baby.hs
*Main> numLongChians
66
Elxiir
awesome.exs
defmodule Awesome do
def chain(1), do: [1]
def chain(n) when rem(n, 2) == 0, do: [n | div(n, 2) |> chain()]
def chain(n), do: [n | (n * 3 + 1) |> chain()]
def num_long_chains do
# Pipe operator is great!!!
1..100
|> Enum.map(&chain/1)
|> Enum.map(&Enum.count/1)
|> Enum.filter(&(&1 > 15))
|> Enum.count()
end
end
scanl
Haskell
Prelude> scanl (+) 0 [3,5,2,1]
[0,3,8,10,11]
Elixir
iex(68)> Enum.scan([3,5,2,1], 0, &(&1 + &2))
[3, 8, 10, 11]
奇数の平方根で10000より小さいものの総和を求める
Haskell
Prelude> oddSquareSum = sum . takeWhile (<10000) . filter odd $ map (^2) [1..]
Prelude> oddSquareSum
166650
- 関数合成というものを利用した書き方だそうです
Elixir
iex> (
...> 1..(:math.sqrt(10000) |> round)
...> |> Enum.filter(&(rem(&1, 2) == 1))
...> |> Enum.map(&(&1 * &1))
...> |> Enum.filter(&(&1 < 10000))
...> |> Enum.sum()
...> )
166650
Haskellのほうにでてきた【カリー化】とか【部分適用】、【関数適用】、【関数合成】あたりはあんまり理解が進んでいませんが、Elixirでは|>
を使うと似たようなことをうまく書ける印象を持ちました
Elixirで&記法を使って引数に指定する関数を書くと、HaskellとElixirで書き方がよく似た形になるようにおもいました
Wrapping Up
-
|>
Pipe operatorとは大好きです - I like it!
- Enjoy! with Elixir