Elixir 練習問題 ControlFlow-3

こんな感じでいいかな。

defmodule OK do
  def ok!({:ok, data}), do: data
  def ok!(_), do: raise("Error occured.")
end

file = OK.ok! File.open("somefile")
^o^ > elixir practice_12_3.exs
warning: variable file is unused
  practice_12_3.exs:6

** (RuntimeError) Error occured.
    practice_12_3.exs:3: OK.ok!/1
    (elixir) lib/code.ex:363: Code.require_file/2

ちなみに、somefile の代わりに存在するファイルで試してみると次のようになる。

^o^ > elixir practice_12_3.exs
warning: variable file is unused
  practice_12_3.exs:6

エラーは起きない。警告が出てるのは file 変数を使ってないからだね。

Elixir 練習問題 ControlFlow-1

気が付けば前回の Elixir のエントリからひと月も経ってしまった。
ま、気を取り直して。FizzBuzz を case を使って書け、と。

defmodule FizzBuzz do
  def fizzbuzz(n) do
    case {rem(n, 3), rem(n, 5), n} do
      {0, 0, _} -> "FizzBuzz"
      {0, _, _} -> "Fizz"
      {_, 0, _} -> "Buzz"
      {_, _, m} -> to_string(m)
    end
  end
end

(1..20)
  |> Enum.each(&IO.puts(FizzBuzz.fizzbuzz(&1)))
^o^ > elixir practice_12_1.exs
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

Elixir 練習問題 StringsAndBinaries-7

defmodule Sales do
  def csv_to_list(filename) do
    {:ok, file} = File.open(filename, [:read])
    headers = (IO.read(file, :line) |> parse_csv_atom)
    IO.stream(file, :line)
    |> Enum.map(fn l -> parse_csv(l) end)
    |> Enum.map(fn sales -> Enum.zip(headers, sales) end)
  end

  defp parse_csv_atom(line) do
    String.trim(line, "\n")
    |> String.split(",")
    |> Enum.map(&String.to_atom/1)
  end

  defp parse_csv(line) do
    [id, ship_to, net_amount] = (String.trim(line, "\n") |> String.split(","))
    [String.to_integer(id), String.to_atom(String.trim(ship_to, ":")), String.to_float(net_amount)]
  end
end

IO.inspect Sales.csv_to_list("sales.csv")
^o^ > cat sales.csv
id,ship_to,net_amount
123,:NC,100.00
124,:OK,35.50
125,:TX,24.00
126,:TX,44.80
127,:NC,25.00
128,:MA,10.00
129,:CA,102.00
130,:NC,50.00

^o^ > elixir practice_11_7.exs
[[id: 123, ship_to: :NC, net_amount: 100.0],
 [id: 124, ship_to: :OK, net_amount: 35.5],
 [id: 125, ship_to: :TX, net_amount: 24.0],
 [id: 126, ship_to: :TX, net_amount: 44.8],
 [id: 127, ship_to: :NC, net_amount: 25.0],
 [id: 128, ship_to: :MA, net_amount: 10.0],
 [id: 129, ship_to: :CA, net_amount: 102.0],
 [id: 130, ship_to: :NC, net_amount: 50.0]]

Elixir 練習問題 StringsAndBinaries-6

defmodule Str do
  def capitalize(word) do
    String.to_charlist(word)
    |> to_upper([])
    |> to_string
  end

  defp to_upper([], result) do
    Enum.reverse(result)
  end
  defp to_upper([32|tail], result) do
    to_upper(tail, [32|result])
  end
  defp to_upper([?.|tail], result) do
    to_upper(tail, result)
  end
  defp to_upper([c|tail], result) do
    to_lower(tail, [upcase(c)|result])
  end
  defp upcase(c) when c <= ?Z, do: c
  defp upcase(c),              do: c - 32

  defp to_lower([], result) do
    Enum.reverse(result)
  end
  defp to_lower([32|tail], result) do
    to_lower(tail, [32|result])
  end
  defp to_lower([?.|tail], result) do
    to_upper(tail, [?.|result])
  end
  defp to_lower([c|tail], result) do
    to_lower(tail, [lowcase(c)|result])
  end
  defp lowcase(c) when c >= 97, do: c
  defp lowcase(c),              do: c + 32
end


IO.puts Str.capitalize("oh. a DOG. woof. ")
^o^ > elixir practice_11_6.exs
Oh. A dog. Woof.

Elixir 練習問題 StringsAndBinaries-5

defmodule Str do
  def center(slist) do
    len = longest(slist)
    slist
    |> Enum.map(fn s -> _center(s, len) end)
    |> Enum.each(&IO.puts/1)
  end

  defp longest(slist) do
    slist
    |> Enum.map(&String.length/1)
    |> Enum.max
  end

  defp _center(str, len) do
    l = div(len - String.length(str), 2)
    String.duplicate(" ", l) <> str
  end
end


Str.center(["cat", "zebra", "elephant"])
^o^ > elixir practice_11_5.exs
  cat
 zebra
elephant

Elixir 練習問題 StringsAndBinaries-4

defmodule Calculater do
  def calculate(charlist) do
    parse(charlist)
    |> calc
  end

  defp parse(charlist) do
    tokenize(charlist, [[]])
    |> _parse
  end

  defp tokenize([], result) do
    Enum.reverse(result)
  end
  defp tokenize([32|tail], result) do
    tokenize(tail, [[] | result])
  end
  defp tokenize([head|tail], [cur|rest]) do
    tokenize(tail, [cur ++ [head] | rest])
  end

  defp _parse([opnd1, op, opnd2]) do
    num1 = number(opnd1, 0)
    num2 = number(opnd2, 0)
    {op, num1, num2}
  end

  defp number([], value) do
    value
  end
  defp number([digit|tail], value) do
    number(tail, value * 10 + digit - ?0)
  end

  defp calc({'+', n1, n2}), do: n1 + n2
  defp calc({'-', n1, n2}), do: n1 - n2
  defp calc({'*', n1, n2}), do: n1 * n2
  defp calc({'/', n1, n2}), do: n1 / n2
end


IO.puts Calculater.calculate('123 + 27')
IO.puts Calculater.calculate('123 - 23')
IO.puts Calculater.calculate('100 * 2')
IO.puts Calculater.calculate('100 / 5')
^o^ > elixir practice_11_4.exs
150
100
200
20.0

Elixir 練習問題 StringsAndBinaries-3

ここのところ Ubuntu ばかりやっていたので、Elixir は久しぶり。

今日の練習問題は、['cat' | 'dog'] という式が、なぜ ['cat', 100, 111, 103] になるのかという問題。まずは実際に確かめてみよう。

^o^ > iex
Eshell V8.0  (abort with ^G)
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> ['cat' | 'dog']
['cat', 100, 111, 103]

まあ、答えは簡単だ。シングルクォートで囲まれた 'dog' は実はただの整数のリストで、たまたまその整数すべてが文字コードとして解釈できるから 'dog' になっているにすぎない。

iex(2)> [100, 111, 103]
'dog'

ところが、このリストの先頭に 'cat' を追加してしまうと、すべてが文字コードとして解釈できる整数ではなくなってしまう。もう少し正確に言うと、リストの最初の要素がリスト、残りが整数になってしまっている。だから、後ろの3要素は文字ではなく文字コードで表示されている。

Elixir 練習問題 StringsAndBinaries-2

アナグラムの判定。パラメータがダブルクォートの文字列じゃなくて、シングルクォートの文字列なら簡単だ。シングルクォートの文字列はただのリストだから、ソートして比べればいい。

defmodule Anagram do
  def anagram?(word1, word2) do
    Enum.sort(word1) == Enum.sort(word2)
  end
end
^o^ > iex practice_11_2.exs
Eshell V8.0  (abort with ^G)
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Anagram.anagram?('hello', 'world')
false
iex(2)> Anagram.anagram?('hello', 'olleh')
true

プログラミングElixir 読み終わった

とりあえず一通り読み終わった。けど、第II部の並行プログラミング以降はかなり消化不良。特にマクロのあたりなんかはほとんどついていけなかった。
まあ、いい。今年はまだ10カ月以上ある。少しずつやっていこう。

[amazonjs asin=”4274219151″ locale=”JP” title=”プログラミングElixir”]