将函数传递给get_in()时如何工作?

7stud

在“编程Elixir 1.6”中,有以下示例:

authors = [
  %{name: "José", language: "Elixir"},
  %{name: "Matz", language: "Ruby"},
  %{name: "Larry", language: "Perl"}
]

languages_with_an_r = fn (:get, collection, next_fn) ->
  for row <- collection do
    if String.contains?(row.language, "r") do
      next_fn.(row)
    end
  end
end

IO.inspect get_in(authors, [languages_with_an_r, :name])
#=> ["José", nil, "Larry"]

我对示例有一些疑问:

  1. 传递给您的函数get_in()由Elixir调用,而Elixir传递给该函数的第一个参数是atom :get这有什么用?

  2. Elixir传递给函数的第三个参数是绑定到的函数next_fn它在文档中的哪里说明该函数需要多少个参数?该功能有什么作用?我们应该如何使用next_fn在我看来,该for构造已经在列表中的每个地图上进行了迭代,因此名称next_fn甚至意味着什么?next_fn用来以某种方式标记进一步审议的行?

  3. 结果列表中的零是哪里来的?

而且,我要说的是:该示例是我在任何编程书籍中都看到过的最糟糕的示例之一,因为没有足够的讨论该示例,而且文档get_in()很烂。这意味着至少有三个人不了解get_in():我,戴夫·托马斯(Dave Thomas)和编写文档的人-因为如果您无法解释某些内容,他们自己就不会理解。

编辑:我在源代码中找到了这一点

def get_in(data, [h | t]) when is_function(h), 
  do: h.(:get, data, &get_in(&1, t))

什么是&1参考呢?data为什么不只是使用data呢?

7stud

好吧,我已经在IEX中玩了一下:

iex(13)> mymax = fn x -> &max(&1, x) end
#Function<6.99386804/1 in :erl_eval.expr/5>

iex(15)> max_versus_3 = mymax.(3)
#Function<6.99386804/1 in :erl_eval.expr/5>

iex(16)> max_versus_3.(4)
4

iex(17)> max_versus_3.(2)
3

看起来该语法&max(&1, 3)返回了匿名函数:

fn (arg) -> max(&1, 3)

当周围范围中的x = 3时,语法&max(&1, x)还将返回该函数:

fn (arg) -> max(&1, 3)

并且&1将成为任何一个ARG匿名函数调用。

在此示例中:

authors = [
  %{name: "José", language: "Elixir"},
  %{name: "Matz", language: "Ruby"},
  %{name: "Larry", language: "Perl"}
]

languages_with_an_r = fn (:get, collection, next_fn) ->
  for row <- collection do
    if String.contains?(row.language, "r") do
      next_fn.(row)
    end
  end
end

IO.inspect get_in(authors, [languages_with_an_r, :name])
#=> ["José", nil, "Larry"]

拨打get_in()这里的电话

IO.inspect get_in(authors, [languages_with_an_r, :name])

与源代码中的以下函数定义匹配:

def get_in(data, [h | t]) when is_function(h), 
    do: h.(:get, data, &get_in(&1, t))

这将创建以下绑定:

data = authors
h = languages_with_an_r
t = [:name]`

然后Elixir执行该函数的主体并调用:

h.(:get, data, &get_in(&1, t))

等效于:

languages_with_an_r.(
     :get, 
     authors, 
     fn (arg) -> get_in(&1, [:name])

创建绑定:

next_fn = fn (arg) -> get_in(&1, [:name])

因此,在作者的示例行中:

next_fn.(row)

相当于调用:

fn (row) -> get_in(&1, [:name])

导致get_in()执行以下参数:

get_in(row, [:name])

然后调用会get_in()返回与中的:name对应的值row我认为,如果将的定义中的参数变量languages_with_an_r()重命名,作者的示例将更加清晰

languages_with_an_r = fn (:get, collection, search_for_next_key_in) ->
      for row <- collection do
        if String.contains?(row.language, "r") do
          search_for_next_key_in.(row)
        end
      end
    end

该代码将只搜索的:name关键row,如果row.language包含“R”。

最后,以下代码片段显示了nil来源:

iex(5)> for x <- [1, 2, 3] do       
...(5)> if x == 1_000_000, do: x+1    
...(5)> end
[nil, nil, nil]

像Ruby中一样,do blockElixir中的a似乎返回了最后一个被求值的表达式的值。而且,当ado block不评估表达式时,默认情况下会do block返回nil因此,如果ifrow.language不包含“ r”,则将跳过if语句,并且ifdo block不评估任何表达式,因此默认情况下do block返回nil。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章