LoginSignup
12
6

More than 3 years have passed since last update.

「プログラミングElixir」の練習問題 解答例

Last updated at Posted at 2020-07-07

エリジョ「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
  1. Timesモジュールに、引数を3倍にする triple関数を拡張しよう。
  2. iex でその結果を実行しよう。ファイルをコンパイルする二つの方法を使ってみよう。
  3. 引数を4倍にするquadruple関数を追加してみよう(多分、double関数をつかうんじゃないかな)。
解答例
Times.exs
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)を、再帰を使って実装し、実行してみよう。この関数は、新しいファイルのモジュールの中に書くこと。

解答例
Sum.exs
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))だ。

解答例
Gcd.exs
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)。

解答例
Chop.exs
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 
12
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
6