【问题标题】:Elixir: I'm getting an error from a simple addition functionElixir:我从一个简单的加法函数中得到一个错误
【发布时间】:2019-10-11 06:50:24
【问题描述】:

我在 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

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

【问题讨论】:

  • 与 Erlang 不同,Elixir 中的变量是小写的。大写的名称被视为原子。而且您显然不能分配给原子,也不能添加数字。此外,按照惯例(非强制性),函数名称应为蛇形 (add_to_tuple.)
  • @AlekseiMatiushkin 谢谢,它工作。但是,我尝试修改函数,以便在元组中包含一个原子并得到一个错误。检查更新 1。
  • 原子是一个生命周期常数。变量是小写的。你可以接受任何东西,包括原子,变量。你正在做的是模式匹配。尝试将A 作为第一个参数传递——这样会起作用。

标签: elixir


【解决方案1】:

在你的函数定义中:

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 尝试下一个函数子句,依此类推。找到匹配项时,将执行该函数子句。如果没有找到匹配项,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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-02
    • 2020-10-07
    • 1970-01-01
    • 2016-02-26
    • 1970-01-01
    • 2013-05-13
    • 2011-10-14
    相关资源
    最近更新 更多