Elixir:我从一个简单的加法函数中得到一个错误

时间:2019-05-24 16:36:46

标签: elixir

我在Elixir中创建了一个简单的函数,该函数将元组的值加1。

我将模块命名为Test,并将函数命名为addToTuple。它有一个参数,就是元组本身。

def addToTuple({X,Y}) do 
   {X,Y+1}
end

我通过iex编译了模块,并收到以下警告:

 warning: this expression will fail with ArithmeticError
  Test.ex:68

第68行引用{X,Y + 1}。当我使用命令Test.addToTuple({4,5})运行该函数时,我不断收到此错误:

** (FunctionClauseError) no function clause matching in Test.addToTuple/1

    The following arguments were given to Test.addToTuple/1:

        # 1
        {4, 5}

    Test.ex:67: Test.addToTuple/1

我原本希望得到{4,6}。

你知道这是怎么回事吗?

更新1:

我将X和Y更改为小写,并且有效。但是,这次我对函数进行了一些修改以与原子配合使用:

def addToTuple({A,x,y}) do 
    {A,x,y+1}
end

然后我使用以下命令调用该函数:Test.addToTuple({:F,4,5})。我期待得到{:F,4,6}。但是,相反,我得到了这个错误:

** (FunctionClauseError) no function clause matching in Test.addToTuple/1

    The following arguments were given to Test.addToTuple/1:

        # 1
        {:F, 4, 5}

    Test.ex:67: Test.addToTuple/1

我认为使用大写字符会被视为原子?该如何解决?

1 个答案:

答案 0 :(得分:5)

在此处的函数定义中:

def addToTuple({A,x,y}) 

A不是变量,而A是原子,就像一个常数。通常,您这样编写原子:

 :dog
 :"my dog"

但是,还有另一种用于创建原子的语法:您可以省略前导冒号并以大写字母开头,例如:

Dog

Elixir in Action(2nd)称为 别名 (对于原子)。在编译时,Dog被转换为原子:"Elixir.Dog"。检查一下:

iex(5)> Dog == :"Elixir.Dog"
true

返回您的函数定义:

def addToTuple({A,x,y})

唯一与您的函数定义匹配的参数如下:

{A, 1, 3}
{A, "hello", "world"}
{A, [1, 2, 3], [4, 5, 6]}

换句话说,将与功能参数列表中的A“匹配”的唯一事物是A。 Elixir与其他语言不同,在其他语言中,函数定义的参数列表中仅允许变量。在其他语言中,函数定义如下所示:

def go(x, y, z) do  
  ...
end

但是,在elixir中,您可以在参数列表的参数列表中包含 常数 ,如下所示:

def go(1, x, 2, y) do
  ...
end

在该函数定义中,参数包括常量12。如果您这样调用该函数:

go(10, 20, 30, 40)

该函数将不会执行,因为函数参数10, 20, 30, 40与函数参数1, x, 2, y不匹配。调用函数时,函数参数将与函数参数匹配,如下所示:

     go(10, 20, 30, 40)
        |   |   |   |
        V   V   V   V
 def go(1,  x,  2,  y) do

对于该函数调用,elixir执行以下匹配/分配:

    1 = 10
    x = 20
    2 = 30
    y = 40

由于1与10不匹配,因此该功能无法执行。原子A就像整数1和2一样。唯一匹配A的是A

由于elixir允许在函数的参数列表中使用常量,因此您可以定义一系列函数子句,如下所示:

defmodule My do

  def go(1, x) do
   IO.puts x*2
  end   
  def go(2, x) do
   IO.puts x-4
  end
  def go(A, x) do
   IO.puts x+5
  end

end

My.go(2, 3)
My.go(A, 5)

输出:

-1
10

当您调用定义了多个子句的函数时,elixir从第一个子句开始,并尝试将函数调用中的参数与参数列表进行匹配。如果没有匹配项,则elixir尝试下一个函数子句,依此类推。找到匹配项后,将执行该函数子句。如果找不到匹配项,则长生不老药将引发错误。例如,调用:

My.go(B, 3)

导致:

** (FunctionClauseError) no function clause matching in My.go/2    

另一个例子:

defmodule My do

  def calc({:add, x, y}) do
    x + y
  end
  def calc({:subtract, x, y}) do
    x - y
  end
  def calc({:multiply, x, y}) do
    x * y
  end

end

在iex中:

~/elixir_programs$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> My.calc {:add, 10, 5}
15

iex(2)> My.calc {:subtract, 10, 5}
5

iex(3)> My.calc {:multiply, 10, 5}
50