エリジョ「Elixir女子部」プログラミングElixir輪読会#7 振り返りの会 で「プログラミングElixir」の練習問題を解きました。
5章 無名関数
Functions-1
iexの中で、次の関数を作り、呼び出してみよう。
・list_concat.([:a, :b], [:c, :d] ) #=> [:a, :b, :c, :d]
・sum.(1, 2, 3) #=> 6
・pair_tuple_to_list.( {1234, 5678} ) #=> [1234, 5678]
解答例
list_concat = fn (a, b) -> a ++ b end
sum = fn (a, b, c) -> a + b + c end
pair_tuple_to_list = fn (a) -> Tuple.to_list(a) end
Functions-2
引数を3つとる関数を書こう。もし最初の二つの引数が0なら"FizzBuzz"を返す。もし最初の引数が0なら"Fizz"を返す。もし二番目の引数が0なら"Buzz"を返す。そうでなければ三番目の引数を返す。
解答例
fizzbuzz = fn
(0, 0, _) -> "FizzBuzz"
(0, _, _) -> "Fizz"
(_, 0, _) -> "Buzz"
(_, _, a) -> a
end
Functions-3
rem(a, b)という演算子はaをbで割った余りを返す関数だ。引数として整数(n)一つを取り、直前の練習問題 Functions-2で作った関数を呼び出して、rem(n, 3)、rem(n, 5) そしてnを引数としてわさす関数を書いてみよう。引数を、10、11、12、と続けて7回呼び出してみよう。「Buzz,11,Fizz,12,14,FizzBuzz,16」という出力が得られるはずだ。
解答例
fizzbuzz2 = fn( n ) -> fizzbuzz.( rem(n, 3), rem(n,5), n ) end
Functions-4
文字列を一つ取る、prefix関数を書いてみよう。この関数は、二つ目の文字列をとる新しい関数を返す。二番目の関数が呼ばれたときには、一番目の文字列、スペース、そして二番目の文字列をもった文字列を返すようにしよう。
iex> mrs = prefix.("Mrs")
iex> mrs.("Smith")
"Mrs Smith"
解答例
prefix = fn str1 -> (fn str2 -> str1 <> " " <> str2 end) end
Functions-5
&... 記法を使って、次のコードを書き換えてみよう。
・Enum.map [1,2,3,4], fn x -> x + 2 end
・Enum.each [1,2,3,4], fn x -> IO.inspect x end
解答例
Enum.map [1,2,3,4], &(&1 + 2)
Enum.each [1,2,3,4], &IO.inspect/1
6章 モジュールと名前付き関数
ModulesAndFunctions-1,2,3
- Timesモジュールに、引数を3倍にする triple関数を拡張しよう。
- iex でその結果を実行しよう。ファイルをコンパイルする二つの方法を使ってみよう。
- 引数を4倍にするquadruple関数を追加してみよう(多分、double関数をつかうんじゃないかな)。
解答例
defmodule Times do
def double(n), do: n * 2
def triple(n), do: n * 3
def quadruple(n), do: double(n) * 2
end
ModulesAndFunctions-4
1からnまでの整数の合計を計算するための関数 sum(n)を、再帰を使って実装し、実行してみよう。この関数は、新しいファイルのモジュールの中に書くこと。
解答例
defmodule Sum do
def of(0), do: 0
def of(n), do: n + of(n-1)
end
ModulesAndFunctions-5
二つの負ではない整数の最大公約数を求める関数 gcd(x,y)を書いてみよう。代数的には、gcd(x,y)はyが0のときx、そうでなければ gcd(y, rem(x,y))だ。
解答例
defmodule Gcd do
def of(x, 0), do: x
def of(x, y), do: of(y, rem(x, y))
end
ModulesAndFunctions-6
1から1000までの間の数を一つ、思い浮かべて欲しい。
その数を見つける、最も効率的な方法は、範囲の一番小さい値と一番大きな値の真ん中を推測値とすることだ。もし推測値が大きすぎれば、答えはい一番小さい値と、推測値より1小さい値の間にあることになる。もし推測値が小さすぎれば、答えは推測値より1大きい値と、一番大きな値の間にあることになる。
作るべきAPIは、guess(actual, range)だ。rangeはElixirの範囲だ。
出力はこんな感じにしてほしい。
iex> Chop.guess(273, 1..1000)
Is it 500
Is it 250
Is it 375
Is it 312
Is it 281
Is it 265
Is it 273
273
ヒント:
・追加のパラメータ(現在の推測値)を持つヘルパー関数を実装しなければならないだろう。
・div(a,b)関数は整数の除算を行う。
・ガード節は味方になってくれる。
・パターンマッチで、範囲の最小値と最大値のそれぞれのパートにマッチさせることができる(a..b = 4..8)。
解答例
defmodule Chop do
def guess(actual, range) do
min..max = range
guess_val = div(min + max, 2)
IO.puts("Is it #{guess_val}")
_guess(guess_val, actual, range)
end
defp _guess(guess_val, actual, range) when guess_val > actual do
min.._max = range
guess(actual, min..guess_val-1)
end
defp _guess(guess_val, actual, range) when guess_val < actual do
_min..max = range
guess(actual, guess_val+1..max)
end
defp _guess(guess_val, actual, range) when guess_val == actual do
_min.._max = range
IO.puts(guess_val)
end
end
ModulesAndFunctions-7
次のことをするライブラリを見つけ、iexで使ってみよう。(各課題の最後にあるElixirやErlangは、どのライブラリ群をみればよいかを示している)
・浮動小数点を二つの10進数からなる文字列に変換する(Erlang)
・オペレーティングシステムの環境変数を取り出す(Elixir)
・ファイルの拡張子を取り出す(Elixir)
・プロセスのカレントワーキングディレクトリを返す(Elixr)
・JSON文字列を、Elixirのデータ構造に変換する
・オペレーティングシステムのシェルでコマンドを実行する。
解答例
#浮動小数点の変換
io_lib.format("~.2f", [3.14])
#環境変数
System.get_env()
#ファイルの拡張子
Path.extname("Times.exs")
#プロセスのカレントワーキングディレクトリ
Process.get()
#JSONを扱うパッケージ
Poison.decode(json)
#外部コマンド
System.cmd("whoami", [])
7章 リストと再帰
ListsAndRecursion-0
sum関数はアキュームレータを使わずに書くこともできる。
解答例
defmodule MyList do
def sum2(list), do: Enum.reduce(list, fn(x, acc) -> x + acc end)
end
ListsAndRecursion-1
リストと関数を引数に取る mapsum関数を書いてみよう。リストの各要素に関数を適用して、その結果を合計する。
iex> MyList.mapsum [1, 2, 3], &(&1 * &1)
14
解答例
defmodule MyList do
def mapsum(list, func), do: _mapsum(list, 0, func)
defp _mapsum([], total, _func) do
total
end
defp _mapsum([head | tail], total, func) do
_mapsum(tail, func.(head) + total, func)
end
end
ListsAndRecursion-2
リストの要素の最大数を返す max(list) を書いてみよう。
解答例
defmodule MyList do
def max(list), do: _max(list, 0)
defp _max([], value) do
value
end
defp _max([head | tail], value) when value > head do
_max(tail, value)
end
defp _max([head | tail], value) when value <= head do
_max(tail, head)
end
end
ListsAndRecursion-3
Elixirのシングルクォートで囲まれた文字列は、実際は個々の文字コードのリストだ。 ceasar(list, n)関数を書いてみよう。リストの各要素に n を足して、足した結果、その文字が z を超えたら a にぐるっと廻るようにする
解答例
defmodule MyList do
def caesar(list, n), do: _caesar(list, n, [])
defp _caesar([], n, value) do
value
end
defp _caesar([head | tail], n, value) when head + n > ?z do
_caesar(tail, n, value ++ [head + n - (?z - ?a)])
end
defp _caesar([head | tail], n, value) when head + n <= ?z do
_caesar(tail, n, value ++ [head + n])
end
end
ListsAndRecursion-4
from から to までの数のリストを返す関数 MyList.span(from, to)を書いてみよう。
解答例
defmodule MyList do
def span(from, to), do: _span(from, to, [])
defp _span(from, to, value) when from > to do
value
end
defp _span(from, to, value) when from <= to do
_span(from + 1, to, value ++ [from])
end
end