Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Elixirで、引数を一つとり`:pop`を返す関数をみなさんはどう書きますか?

解決したいこと

Elixirで、引数を一つとり:popを返す関数を書きたいです。
この書き方で目的は果たせます。

fn _value -> :pop end

上記以外の他の書き方はありますでしょうか。
特に&記法での書き方はできますでしょうか。

なぜそんなことをしたいの?

Kernel.get_and_update_in/3 で、三番目の引数fun:popを返すようにしたいです。

funは以下の関数が求められています。
公式ドキュメントから抜粋します。

The fun argument receives the value of key (or nil if key is not present) and must return one of the following values:

- a two-element tuple {current_value, new_value}. In this case, current_value is the retrieved value which can possibly be operated on before being returned. new_value is the new value to be stored under key.

- :pop, which implies that the current value under key should be removed from the structure and returned.

日本語でポイントのみ書いてみます。
第2引数keysで指定されたキーに対応する値が関数に渡されます。
その関数は、次の2つのうちいずれかを返す必要があります。

  • {current_value, new_value}
  • :pop

前置きが長くなりました。
四の五の言わずに、Kernel.get_and_update_in/3の実行例をみてみます。

{current_value, new_value}

まずは①番目の{current_value, new_value}を返す関数を指定する方法をみてみます。

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
%{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["john", :age], &{&1, &1 * 1000})
{27, %{"john" => %{age: 27000}, "meg" => %{age: 23}}}

上を実行してももちろん、usersの値が書き換わっているわけではないです。

iex> users
%{"john" => %{age: 27}, "meg" => %{age: 23}}

:pop

次に②番目の:popを返す関数を指定する方法をみてみます。

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
%{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["john", :age], fn _value -> :pop end)
{27, %{"john" => %{}, "meg" => %{age: 23}}}

fn _value -> :pop endこの書き方を&記法等、他の書き方はないのかな〜 という質問です。

自分で試したこと

なんとなく、fn _value -> :pop endしか書きようがないようにおもっています。
ただ、私の思い込みかもしれないのと、Qiitaの質問投稿を初めてやってみようとおもった次第です。

ご回答お待ちしております。
Thanks for your time.

Advent Calendar 2022

Advent Calendar 2022 23日目[^1]の記事です。
I'm ready for 12/25,2022 :santa::santa_tone1::santa_tone2::santa_tone3::santa_tone4::santa_tone5:
I'm looking forward to 12/25,2022 :santa::santa_tone1::santa_tone2::santa_tone3::santa_tone4::santa_tone5:
I can't wait for 12/25,2022 :santa::santa_tone1::santa_tone2::santa_tone3::santa_tone4::santa_tone5:
私のAdvent Calendar 2022 一覧

Elixirを楽しんでいますか:bangbang::bangbang::bangbang:

$\huge{Enjoy\ Elixir🚀}$

1

3Answer

この場合はpop_in/2を使うべきだと思います

iex> pop_in(users, ["john", :age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}
2Like

Comments

  1. @torifukukaiou

    Questioner

    なるほど! そもそも問題に立ち返って、input -> outputを見つめ直すと、 `pop_in/2` という関数があるわけですね。はじめて知りました!
    優勝です!
    ありがとうございます!!!

    # input

    ```elixir
    users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
    ```

    # output


    ```elixir
    {27, %{"john" => %{}, "meg" => %{age: 23}}}
    ```

今いろいろ試してみましたが、同じ結論になりました。
&を使うためには最低一度は引数を使う必要があるようなので、無理やり無駄な処理を挟むことにより別の表現をすることは出来はしました。

get_and_update_in(users, ["john", :age], &elem({:pop, &1}, 0))
get_and_update_in(users, ["john", :age], &hd([:pop, &1]))

しかしながら、無駄な処理をするメリットがないので、結局もとのfn _value -> :pop endがよい気がします。fn _ -> :pop endとすれば十分短いですし。

1Like

Comments

  1. @torifukukaiou

    Questioner

    優勝です。
    `fn _ -> :pop end`
    やっぱりこれがいいとおもいます。
    ありがとうございます!

いい方法が見つかりました!多分これが一番短いです。

get_and_update_in(users, ["john", :age], &(:pop || &1))
get_and_update_in(users, ["john", :age], &:pop || &1)
1Like

Comments

  1. @torifukukaiou

    Questioner

    準優勝です。
    `&:pop || &1`
    はテクニックという意味では短絡評価とかなかなかおもしろいです。
    ただ、あとで読み返したときになにしているんだっけ? って私は忘れる自信があります。

Your answer might help someone💌