3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

(初学者向け)すべてiexで簡単に、Enum.mapの動きをデバッグで見たい(dbg 編)~ dbgの解説付き ~

Last updated at Posted at 2023-01-01

はじめに

Enum.mapの処理手順をすべてiexで動かしながら、いかに手軽に確認できるかを考えました。
前回、
(初学者向け)すべてiexで簡単に、Enum.mapの動きをデバッグで見たい(IO.inspect 編)で書いたのですが、納得いくものではなったです。そこで、Elixirの偉大な先輩方にElixir1.14からdbgあるよと教えていただき、改めて作り直しました。

余談(ポエム)

dbgには、さまざま機能がありますが、具体的に何が出来るのか、身体で覚えていきたいと思います。
身体で覚えるとは、自ら手を動かしてハマって試し、「わかる」から「できる」になるという意味合いです。

身体で覚えるElixirシリーズ書いて行こうかな。確実に女性には嫌われるけど。lol

やりたいのは、「すべてiexで簡単に、Enum.mapの動きをデバッグで見たい」という事です。
普通に関数で書けば実現できるのは前回書きましたが、iexで楽してやりきりたい。そうしたら、今後気楽にiexでデバッグなんかもしながら、何でも分かった気になれるのではないかという淡い期待を元に書いています。

余談に付き合って頂きありがとうございます。

dbgとは、から書いていきます。

dbg/2 とは

Elixir v1.14から、デバッグの強化のためにdbg/2マクロが提供されました。

dbg/2は、BEAM VM上でイベントをトレースする機能を提供します。関数の呼び出しと戻り、例外の発生、メッセージの送受信、スポーン、終了、リンク、スケジューリング、ガベージコレクションなどの多くのイベント(ノードの集まり)の端から端までトレースすることができます。

IExの有無にかかわらず使用できるマクロです。
dbg/2はKernelモジュールに属し、どこからでも呼び出せます。
引数なしのdbg()で、現在束縛している変数の情報を表示します。
今回は、dbg()しか使わないので、詳細を知りたい方は公式を参考にしてください。
hexdocs dbg はコチラ

※Elixir v1.14からしか利用できないので、ご注意ください。
公式のElixir v1.14 releasedはこちら

dbg()の基本的な使い方

例えば、以下のような関数を書きます。

dbg()は、実行されるタイミングで、その時点で束縛している変数の中身を出力してくれます。

また、dbg()を入れた関数を実行すると、リプライがpry>に変わり、nを入力する毎に、次のdbg()を実行してくれます。

言葉でいうと話が長く伝わりにくいので、実際に動かしてみてください。

実行結果は以下に貼り付けております。

$ iex
Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)
iex> defmodule Ex3 do
iex>   def add(a) do
iex>     dbg()
iex>     r = a + 1
iex>     dbg()
iex>     r = r * 3
iex>     dbg()
iex>   end
iex> end
{:module, Ex3,
 <<70, 79, 82, 49, 0, 0, 18, 252, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 3,
   0, 0, 0, 30, 10, 69, 108, 105, 120, 105, 114, 46, 69, 120, 51, 8, 95, 95,
   105, 110, 102, 111, 95, 95, 10, 97, 116, ...>>, {:add, 1}}
iex> Ex3.add(1)
Break reached: Ex3.add/1 (iex:3)
pry> n
binding() #=> [a: 1]

Break reached: Ex3.add/1 (iex:5)
pry> n
binding() #=> [a: 1, r: 2]

Break reached: Ex3.add/1 (iex:7)
pry> n
binding() #=> [a: 1, r: 6]

[a: 1, r: 6]

Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)

こんな感じでIO.inspectと比べるとシンプルにデバッグできます。

でも、これって行単位の実行だからEnum.mapの中の動きはトレース出来なくない?
と今更ながら気付くのです。この辺りは、Elixirの偉大な先輩方は自分で考える領域を残してくれています。結局自分で考えないと、記憶にも記録にも残らないのでとてもありがたいです。

もう一度、確認します。やりたかった事は何か?
「すべてiexで簡単に、Enum.mapの動きをデバッグで見たい」という事です。

まず、前回のおさらいです。

デバッグしたいのはこの処理です。

iex(2)> Enum.map([1, 2, 3], &(&1 + 1))
[2, 3, 4]

前回までは、IO.inspectを使って、このような形でインプット、アウトプットを出力し、処理状況が見えるようになりました。

iex(1)> Enum.map([1, 2, 3], &(IO.inspect(&1) + 1))
1
2
3
[2, 3, 4]

今回は、上記を改良していきます。
まず、何も考えないなら、IO.inspectdbgに置き換えます。

iex(1)> Enum.map([1, 2, 3], &(dbg(&1) + 1))
Break reached: iex:1
pry(1)> n
x1 #=> 1

Break reached: iex:1
pry(1)> n
x1 #=> 2

Break reached: iex:1
pry(1)> n
x1 #=> 3

[2, 3, 4]

Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)

dbgの出力結果を見ると、&1x1として、中身が表示されました。
いい。これでいいんだけど、前にdbg入れたらどうなるのかな?

iex(3)> Enum.map([1, 2, 3], &(dbg(&1 + 1))
...(3)> )
Break reached: iex:3
pry(1)> n
x1 + 1 #=> 2

Break reached: iex:3
pry(1)> n
x1 + 1 #=> 3

Break reached: iex:3
pry(1)> n
x1 + 1 #=> 4

[2, 3, 4]

Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)

計算過程x1 + 1 #=> 2が見えます。
でも、x1 #=> 1は消えました。

パイプで繋いだら見え方変わるかな?
ということで、やってみます。

iex(1)> Enum.map([1, 2, 3], &(&1 + 1 |> dbg()))
Break reached: iex:1
pry(1)> n
x1 + 1 #=> 2

Break reached: iex:1
pry(1)> n
x1 + 1 #=> 3

Break reached: iex:1
pry(1)> n
x1 + 1 #=> 4

[2, 3, 4]

Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)

結果は同じです。

という事は、これまでの合わせ技で以下のようにdbgを2つ書けばいいのではと考えました。

iex(1)> Enum.map([1, 2, 3], &dbg(dbg(&1) + 1))
Break reached: iex:1
pry(1)> n
Break reached: iex:1
pry(1)> n
x1 #=> 1

dbg(x1) + 1 #=> 2

Break reached: iex:1
pry(1)> n
Break reached: iex:1
pry(1)> n
x1 #=> 2

dbg(x1) + 1 #=> 3

Break reached: iex:1
pry(1)> n
Break reached: iex:1
pry(1)> n
x1 #=> 3

dbg(x1) + 1 #=> 4

[2, 3, 4]

Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)

うん。理想的な結果だと思います。

しかし、課題は2つ残りました。

  1. dbgを2回も書かないといけないという余分なコーディング
  2. リプライがpry(1)>に変わった後、nを2回実行しないと一つ目の出力結果が表示されないという余分な操作。

2つ目については、偉大な先輩からアドバイスを頂き、--no-pryあるよと教えて頂きました。
必要な変数の束縛内容は出力されているのだから、止めてpry(のぞき見)する必要もないと気が付き、以下の通りiex --no-pryでスッキリ出力しました。

$ iex --no-pry
Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)

iex> Enum.map([1, 2, 3], &dbg(dbg(&1) + 1))
[iex:1: (file)]
x1 #=> 1

[iex:1: (file)]
dbg(x1) + 1 #=> 2

[iex:1: (file)]
x1 #=> 2

[iex:1: (file)]
dbg(x1) + 1 #=> 3

[iex:1: (file)]
x1 #=> 3

[iex:1: (file)]
dbg(x1) + 1 #=> 4

[2, 3, 4]

確かに、課題は残りましたが十分だと思います。

また、解決策が分かれば追記します!

もっといい方法があればご教授いただければ幸いです。

3
0
1

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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?