【问题标题】:Are multi-arity anonymous functions possible in Erlang?Erlang 中是否可以使用多参数匿名函数?
【发布时间】:2016-04-11 17:18:05
【问题描述】:

我只能创建分配给变量的匿名函数,如下所示:

Foo = fun(X) -> X end.

我知道如果你在一个模块中,你可以这样做:

foo(X) -> X.
foo(X, Y) -> X + Y.
% i.e. foo(3) = 3, foo(3,4) = 7

我的问题是:匿名函数可以做到吗?

一篇博文(对不起,我现在丢了)让我觉得你可以做这样的事情:

Foo = fun(X) -> X;
         (X, Y) -> X + Y
      end.

但这不起作用,因为我收到“头部不匹配”错误。

【问题讨论】:

  • 您只能为模式匹配执行此操作,即Foo = fun({x,y}) -> one; ({a,b}) -> two end,除非您只接受一个列表,例如Foo = fun([X]) -> X; ([X,Y]) -> {X,Y} end,否则我想不出一种方法可以做您想做的事情,调用@ 987654326@和Foo([a,b])
  • 是的,除了接受列表之外,我什么也找不到。在实际情况下,传递的每个变量都是不同的东西,所以一种选择是传递一个 proplist(我认为这是一个惯用的映射,我是 Erlang 的新手)并这样做大大地。如果您想要甜蜜的业力,请让您的评论成为答案:P
  • 其实这是一个非常有趣的问题,我从来没有注意到或想过这个......
  • Name + Arity 是函数的identity。在匿名函数的情况下,您缺少名称。在一个真正的程序中你不应该有一个你不知道你正在拨打的电话的数量的情况。这种情况怎么会存在?在 FP 中弹出这么多 1 的 lambda 的原因是由于列表(即,一个接一个地迭代)。如果您不知道调用匿名函数的数量,您将如何编写代码?即使你可以为什么你会那样做?不过请记住,您可以使用列表或元组参数正确编写可变参数函数。
  • @SCdF {M,F,A} 当然也是一个论据。如果您可以控制函数的传递并控制它的调用,我认为您必须这样做,否则您将符合特定的 API / 接口,那么您可以传递 {M,F,A}{M,F}价值。这也意味着您没有浮动的匿名函数,如果它们停留的时间足够长,当您进行节点/版本升级时,它们可能会破坏。

标签: erlang


【解决方案1】:

您只能进行模式匹配:

Foo = fun ({x,y}) -> one;
          ({a,b}) -> two
          end

你可以接受一个列表:

Foo = fun ([X])   -> X;
          ([X,Y]) -> {X,Y}
          end

我太好奇了,不想看性能,所以我快速进行了基准测试。这远非完美,只是为了说明可能的情况,实际上应该计算平均值、标准差、平均值、最小值和最大值,但我选择了最少时间进行 1,000,000 次调用。

我使用了这个代码:

-module(foo).

-export([run/1, norm1/0, norm2/0, norm3/0, norm4/0, list1/0, list2/0, list3/0, list4/0]).

-define(COUNT, 1000000).


run(F) ->
    T = lists:foldl(
        fun(_,Min) ->
            T = ?MODULE:F(),
            if T < Min -> T; true -> Min end
            end,
        ?MODULE:F(),
        lists:seq(1,99)
        ),
    T
    .

norm1() ->
    Foo = fun(N) -> N * N end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo(A) end, lists:seq(1,?COUNT))
        end),
    T
    .

norm2() ->
    Foo = fun(N, M) -> N * M end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo(A,A) end, lists:seq(1,?COUNT))
        end),
    T
    .

norm3() ->
    Foo = fun(M, N, M) -> N * M end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo(A,A,A) end, lists:seq(1,?COUNT))
        end),
    T
    .

norm4() ->
    Foo = fun(N, M, N, M) -> N * M end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo(A,A,A,A) end, lists:seq(1,?COUNT))
        end),
    T
    .

list1() ->
    Foo = fun([N]) -> N * N end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo([A]) end, lists:seq(1,?COUNT))
        end),
    T
    .

list2() ->
    Foo = fun([N, M]) -> N * M end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo([A,A]) end, lists:seq(1,?COUNT))
        end),
    T
    .

list3() ->
    Foo = fun([_, N, M]) -> N * M end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo([A,A,A]) end, lists:seq(1,?COUNT))
        end),
    T
    .

list4() ->
    Foo = fun([_, _, N, M]) -> N * M end,
    {T,_} = timer:tc(fun() ->
        lists:map(fun(A) -> Foo([A,A,A,A]) end, lists:seq(1,?COUNT))
        end),
    T
    .

这是结果:

1> foo:run(norm1).
44820
2> foo:run(norm2).
48959
3> foo:run(norm3).
50328
4> foo:run(norm4).
50402
5> 
5> foo:run(list1).
50463
6> foo:run(list2).
58948
7> foo:run(list3).
60829
8> foo:run(list4).
86604
9> 

性能显然将取决于列表的长度,正如人们所期望的那样,因为它必须通过列表工作,并且惩罚比正常调用更大。

在我看来,性能差异并不是很大,但在大多数情况下,如果它是正确的解决方案,至少在您不希望列表中有太多参数的情况下,值得担心!

【讨论】:

    【解决方案2】:

    简单而正确的答案是不,你不能。这实际上是非常合乎逻辑的

    foo(X) -> X.
    foo(X, Y) -> X + Y.
    

    您实际上是在创建 两个 函数:foo/1,一个参数的函数;和foo/2,另一个有两个参数的函数。它们同一个foo。这直接映射到匿名函数 (funs),因此您需要创建两个不同的函数,一个参数之一,另一个参数。

    “head mismatch”错误是抱怨参数数量不同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-22
      • 1970-01-01
      • 2012-08-31
      • 1970-01-01
      • 2023-02-07
      相关资源
      最近更新 更多