Elixir: 不同元数的函数重载

7
有没有一种方法可以定义不同参数数量的重载函数,例如在C#中我只需要这样做:

foo(bar)

或者

foo(bar, baz)

在Elixir中,唯一的方法是将它们放在单独的模块中,这很快会变得混乱。有没有其他解决办法呢?

编辑:我做出了错误的假设。我看到的重载函数示例恰好具有相同的arity,因此我(错误地)认为这是一个要求。函数根据其名称和arity唯一标识,因此您实际上可以重载具有不同arity的函数。

2个回答

13
在Erlang和Elixir中,与许多其他语言(如C#)不同的是,函数是通过其名称和arity唯一标识的。因此,在技术上,foo(bar)foo(bar, baz)是完全不同的函数。但这只是一个技术细节。要在Elixir中编写“重载”函数,您可以编写以下定义:sum
defmodule Math do
  def sum(list),       do: sum(list, 0)
  def sum([], acc),    do: acc
  def sum([h|t], acc), do: sum(t, acc + h)
end

在第一行中,该函数的元数如何为1? - tldr
3
如 @bitwalker 所说,sum/1 和 sum/2 是不同的函数。函数根据名称和元数(arity)进行识别。 - Miroslav Prymek
哦,那是我的误解 - 所有重载函数的示例都具有相同的参数数量,因此我认为这是一个要求。 - tldr
2
如果你看到许多具有相同名称和元数的函数,那是因为其中一些函数在它们的参数上进行模式匹配,就像上面的例子中的 sum([], acc)sum([h|t], acc) 一样。在这种情况下,函数按源代码顺序尝试,因此当您调用 sum([1, 2, 3], 0) 时,它将首先尝试匹配 sum([], acc),但由于 [1, 2, 3] 不是空列表,所以会失败,然后将尝试 sum([h|t], acc),这将成功,因为列表有一个头和一个尾。您不能有两个名称和元数相同但不匹配其参数的函数。 - bitwalker
为了澄清我上面的陈述,以下内容是无效的:sum(list, acc), do: acc + 1 sum(list, acc), do: acc + 2但以下内容是有效的:sum([], acc), do: acc sum(list, acc), do: [h|t] = list; sum(t, acc + h)原因是第一种情况无法区分两个函数子句,这是无效的。然而第二种情况可以。 - bitwalker
显示剩余2条评论

2
在这个页面上,特别是查看第8.3节及其后面的内容。具体来说,是这样的: 函数声明还支持守卫和多个子句。如果一个函数有几个子句,Elixir会尝试每个子句,直到找到与之匹配的子句为止。下面是一个检查给定数字是否为零的函数实现:
defmodule Math do
  def zero?(0) do
    true
  end

  def zero?(x) when is_number(x) do
    false
  end
end

Math.zero?(0)  #=> true
Math.zero?(1)  #=> false

Math.zero?([1,2,3])
#=> ** (FunctionClauseError)

在单个模块中,同一函数名称可以有多个重载(尽管文档中将此概念称为clauses)。


1
这只是显示具有相同arity的函数。正如我在问题中提到的那样,我正在寻找一种重载具有不同arity的函数的方法。 - tldr
1
你在问题中的评论“在Elixir中,唯一的方法是将它们放在单独的模块中”,让我想到你可能没有理解单个模块中多条子句函数的概念。此外,我添加了这个答案是为了其他未来可能看到这个问题的人。 - Onorio Catenacci
我的误解在于一个函数的分句需要具有相同的arity(因为示例中有具有相同arity的子句)。这是不正确的,我已经在我的问题中加入了编辑。由于您的示例也具有相同的arity子句,因此与问题无关。 - tldr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接