動かざることバグの如し

近づきたいよ 君の理想に

Elixirでパイプ中にpryでデバッグしたい

環境

  • Elixir 1.x

コード

Elixirのパイプ演算子便利なんだけど、途中の変数変化を把握しづらい。そこでpryを使ってブレークポイントを仕掛ける。

defmodule Example do
  def pipe do
    1..10
    |> Enum.map(&(&1 * &1))
    |> Enum.filter(&rem(&1, 2) == 0)
    |> (fn(x) -> IEx.pry;x end).()
    |> Enum.take(3)
  end
end

実行

iex(1)> c "index.ex"
warning: redefining module Example (current version defined in memory)
  index.ex:10

[Example]
iex(2)> Example.pipe
Break reached: Example.pipe/0 (index.ex:15)

   13:     |> Enum.map(&(&1 * &1))
   14:     |> Enum.filter(&rem(&1, 2) == 0)
   15:     |> (fn(x) -> IEx.pry;x end).()
   16:     |> Enum.take(3)
   17:   end

pry(1)> x
[4, 16, 36, 64, 100]
pry(2)> respawn

Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
[4, 16, 36]

パイプ演算子中ではない場合

require IEx;

defmodule Example do
  def add(x, y) do
    IEx.pry
    x + y
  end
end

Elixirでファイルの読み書きまとめ

環境

  • Elixir 1.x

基本的な読み書き

File.write/2 で書き込み、 File.read/1 で読み込みできる。

iex> File.write "sample.txt", "Hello"
:ok
iex> File.read "sample.txt"
{:ok, "Hello"}

読み込んだときに2要素のタプルを返してくるのがポイント

こういう感じで分岐できる

case File.read("sample.txt") do
  {:ok, content} ->
    IO.inspect content
  {:error, reason} ->
    IO.puts "error: #{reason}"
  end

追記したい

第三引数に append を渡す

iex> File.write "sample.txt", "Hello"           
:ok
iex> File.write "sample.txt", " World", [:append]
:ok
iex> File.read "sample.txt"                      

エラー時に例外を発生させたい

関数名の最後にビックリマークをつける

iex> File.read! "404.txt"   
** (File.Error) could not read file "404.txt": no such file or directory
    (elixir) lib/file.ex:310: File.read!/1

ElixirでFizzBuzz

condを使う場合

defmodule FizzBuzz do
  def upto(n) do
    cond do
      rem(n, 15) == 0 -> "FizzBuzz"
      rem(n, 3) == 0 -> "Fizz"
      rem(n, 5) == 0 -> "Buzz"
      true -> n
    end
  end
end

for n <- 1..100 do
  IO.puts FizzBuzz.upto(n)
end

パターンマッチを使う場合

defmodule FizzBuzz do
  def upto(n) when rem(n, 15) == 0, do: "FizzBuzz"
  def upto(n) when rem(n, 3) == 0, do: "Fizz"
  def upto(n) when rem(n, 5) == 0, do: "Buzz"
  def upto(n), do: n
end

for n <- 1..100 do
  IO.puts FizzBuzz.upto(n)
end

参考リンク

Elixirで関数呼び出し時にドットを付ける/付けないの違い

環境

  • Elixir 1.x

結論

  • 無名関数の場合はドット(.)が必要
  • 名前付き関数の場合は不要

違いについて

他の言語やったあとにElixirやってると以下のようなエラーをよく起こす。

iex(1)> add = fn a, b -> a + b end
#Function<12.99386804/2 in :erl_eval.expr/5>
iex(2)> add(1, 2) 
** (CompileError) iex:2: undefined function add/2

実際には以下が正しい。

iex(2)> add.(1, 2)                
3

つまり関数呼び出し時にドットが抜けてたのだが、ドットを付けない場合もある。どういう違いがあるのか気になった。

利害は結構単純で、fnからendのキーワードを使って定義された関数は無名関数と呼ばれ、名前の通り関数名がない。

一方、defからendのキーワードで定義された関数は普通?の関数、名前付き関数になり、名前付き関数を呼び出す場合はドットを付けない。

defmodule Mymodule do
  def add(a, b) do
    a + b
  end
end

IO.puts Mymodule.add(1, 2)

逆にドットを付けて実行するとエラーになる。気をつけよう

Mymodule.add.(1, 2)

** (UndefinedFunctionError) function Mymodule.add/0 is undefined or private. Did you mean one of:

      * add/2

    Mymodule.add()
    hoge.exs:28: (file)
    (elixir) lib/code.ex:677: Code.require_file/2