我在Elixir中创建了一个简单函数,该函数将元组的值加1。
我将模块命名为Test,并将函数命名为addToTuple。它采用一个参数,即元组本身。
def addToTuple({X,Y}) do
{X,Y+1}
end
Run Code Online (Sandbox Code Playgroud)
我通过iex编译了模块,并收到以下警告:
warning: this expression will fail with ArithmeticError
Test.ex:68
Run Code Online (Sandbox Code Playgroud)
第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
Run Code Online (Sandbox Code Playgroud)
我期望得到{4,6}。
知道这里发生了什么吗?
更新1:
我将X和Y更改为小写,并且有效。但是,这次我对函数进行了一些修改,使其可以与原子一起使用:
def addToTuple({A,x,y}) do
{A,x,y+1}
end
Run Code Online (Sandbox Code Playgroud)
然后,我使用以下命令调用该函数: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
Run Code Online (Sandbox Code Playgroud)
我以为使用大写字符会被视为原子?如何解决?
在此处的函数定义中:
def addToTuple({A,x,y})
Run Code Online (Sandbox Code Playgroud)
A不是变量,而是A原子,就像常量。通常,您这样编写原子:
:dog
:"my dog"
Run Code Online (Sandbox Code Playgroud)
但是,还有另一种用于创建原子的语法:您可以省略前导冒号,并以大写字母开头该名称,如下所示:
Dog
Run Code Online (Sandbox Code Playgroud)
Elixir在Action(2nd)中称其为别名(原子)。在编译时,Dog被转换为atom :"Elixir.Dog"。看看这个:
iex(5)> Dog == :"Elixir.Dog"
true
Run Code Online (Sandbox Code Playgroud)
返回您的函数定义:
def addToTuple({A,x,y})
Run Code Online (Sandbox Code Playgroud)
与您的函数定义匹配的唯一参数如下所示:
{A, 1, 3}
{A, "hello", "world"}
{A, [1, 2, 3], [4, 5, 6]}
Run Code Online (Sandbox Code Playgroud)
换句话说,A在功能参数列表中唯一可以“匹配” 的是A。Elixir与其他语言不同,在其他语言中,函数定义的参数列表中仅允许变量。在其他语言中,函数定义如下所示:
def go(x, y, z) do
...
end
Run Code Online (Sandbox Code Playgroud)
但是,在elixir中,可以在函数定义的参数列表中包含常量,如下所示:
def go(1, x, 2, y) do
...
end
Run Code Online (Sandbox Code Playgroud)
在该函数定义中,参数包括常量1和2。如果您像这样调用该函数:
go(10, 20, 30, 40)
Run Code Online (Sandbox Code Playgroud)
该函数无法执行,因为函数参数10, 20, 30, 40与函数参数不匹配1, x, 2, y。调用函数时,函数参数将与函数参数匹配,如下所示:
go(10, 20, 30, 40)
| | | |
V V V V
def go(1, x, 2, y) do
Run Code Online (Sandbox Code Playgroud)
对于该函数调用,elixir执行以下匹配/分配:
1 = 10
x = 20
2 = 30
y = 40
Run Code Online (Sandbox Code Playgroud)
由于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)
Run Code Online (Sandbox Code Playgroud)
输出:
-1
10
Run Code Online (Sandbox Code Playgroud)
当您调用已定义多个子句的函数时,elixir从第一个子句开始,并尝试将函数调用中的参数与参数列表进行匹配。如果没有匹配项,则elixir尝试下一个函数子句,依此类推。找到匹配项后,将执行该函数子句。如果找不到匹配项,则长生不老药将引发错误。例如,调用:
My.go(B, 3)
Run Code Online (Sandbox Code Playgroud)
结果是:
** (FunctionClauseError) no function clause matching in My.go/2
Run Code Online (Sandbox Code Playgroud)
另一个例子:
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
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
83 次 |
| 最近记录: |