函数语言中具有相同名称但不同参数的函数

时间:2016-07-12 09:44:14

标签: functional-programming pattern-matching elixir arity

我在Elixir的example中看到了这段代码:

defmodule Recursion do
  def print_multiple_times(msg, n) when n <= 1 do
    IO.puts msg
  end

  def print_multiple_times(msg, n) do
    IO.puts msg
    print_multiple_times(msg, n - 1)
  end
end

Recursion.print_multiple_times("Hello!", 3)

我在这里看到使用不同参数定义两次的相同函数,我想了解这种技术。

我可以在重载函数中查看它们吗?

它是具有不同行为的单个函数,还是这两个不同的函数,如print_only_onceprint_multiple_times

这些功能无论如何都是联系在一起的?

2 个答案:

答案 0 :(得分:3)

通常在函数式语言中,函数由子句定义。例如,在命令式语言中实现Fibonacci的一种方法是以下代码(不是最佳实现):

def fibonacci(n):
  if n < 0:
    return None
  if n == 0 or n == 1:
    return 1
  else:
    return fibonacci(n - 1) + fibonacci(n - 2)

要在Elixir中定义函数,您将执行以下操作:

defmodule Test do
  def fibonacci(0), do: 1
  def fibonacci(1), do: 1
  def fibonacci(n) when n > 1 do
    fibonacci(n-1) + fibonacci(n - 2)
  end
  def fibonacci(_), do: nil
end

Test.fibonacci/1只是一个功能。具有四个子句和arity为1的函数。

  • 第一个子句仅在数字为0时才匹配。
  • 第二个子句仅在数字为1时才匹配。
  • 第三个子句与任何大于1的数字匹配。
  • 最后一个子句匹配任何内容(当变量的值不在子句中使用或与匹配无关时使用_

子句按声明的顺序进行评估,因此Test.fibonacci(2)在前2个子句中失败并与第3个子句匹配,因为2 > 1

将子句视为更强大的if语句。代码看起来更干净。并且对递归非常有用。例如,地图实现(该语言已经在Enum.map/2中提供了一个):

defmodule Test do
  def map([], _), do: []
  def map([x | xs], f) when is_function(f) do
    [f.(x) | map(xs, f)]
  end
end
  • First子句匹配空列表。无需申请功能。
  • 第二个子句匹配一个列表,其中第一个元素(头部)是x,列表的其余部分(尾部)是xsf是一个函数。它将函数应用于第一个元素,并以列表的其余部分递归调用map。

致电Test.map([1,2,3], fn x -> x * 2 end)将为您提供以下输出[2, 4, 6]

因此,Elixir中的函数定义了一个或多个子句,其中每个子句与其余子句具有相同的arity。

我希望这能回答你的问题。

答案 1 :(得分:1)

在您发布的示例中,函数的两个定义具有相同数量的参数:2,此“when”事物是一个保护,但您也可以使用多个参数定义。首先,警卫 - 它们用于表达不能仅仅是匹配的内容,如下面的第二行:

def fac(0), do: 1
def fac(n), when n<0 do: "no factorial for negative numbers!"
def fac(n), do: n*fac(n-1)

- 因为仅仅通过相等/匹配来表达负数是不可能的。

这个 fac 是一个单一的定义,只有三种情况。请注意在参数位置使用常量“0”的酷感:) 你可以把它想象成更好的写作方式:

def fac(n) do
  if n==0, do: 1, else: if n<0, do: "no factorial!", else: n*fac(n-1)
end

或开关盒(甚至看起来非常接近上面):

def fa(n) do
  case n do
    0 -> 1
    n when n>0 -> n*fa(n-1)
    _ -> "no no no"
  end
end

只是“看起来更花哨”。事实上,事实证明,某些定义(例如解析器,小型解释器)在前者中看起来比后者更好。 Nb守卫表达非常有限(我认为你不能在守卫中使用你自己的功能)。

现在真实的,不同数量的论点 - 检查出来!

def mutant(a), do: a*a
def mutant(a,b), do: a*b
def mutant(a,b,c), do: mutant(a,b)+mutant(c)

e.g。

iex(1)> Lol.mutant(2)
4
iex(2)> Lol.mutant(2,3)
6
iex(3)> Lol.mutant(2,3,4)
22

它在方案中的作用有点类似(lambda arg ...) - 认为变异体将其所有参数作为列表并在其上进行匹配。但这一次,长生不老药将突变体视为3种功能,突变体/ 1 突变体/ 2 突变体/ 3 ,并将它们称为这样

所以,回答你的问题:这些不像重载函数,而是分散/分散的定义。您可以在miranda,haskell或sml等函数式语言中看到类似的语言。