美文网首页
Elixir-函数

Elixir-函数

作者: 你期待的花开 | 来源:发表于2018-11-14 17:36 被阅读5次

目录

  • 匿名函数
    • & 操作符
  • 模式匹配
  • 命名函数
    • 函数名字和元数
    • 私有函数
    • 卫兵
    • 参数默认值

匿名函数

匿名函数就是没有名字,他们被用来传递给其他函数。要定义匿名函数,我们需要 fnend关键字,在这两者之间,我们可以定义任意数量的参数和函数体,他们用 -> 分隔开。

iex> sum = fn (a, b) -> a + b end
iex> sum.(2, 3)
5

& 操作符

因为在 Elixir 中使用匿名函数非常常见,所以有一个快捷方式来做这件事:

iex> sum = &(&1 + &2)
iex> sum.(2, 3)
5

在这种简写的模式下,函数的参数可以通过 &1,&2,&3 等来获取。

模式匹配

在 Elixir 中模式匹配不仅限于变量,也可以用在函数签名上。Elixir 使用模式匹配来找到第一个匹配参数的模式,然后执行它后面的函数体。

iex> handle_result = fn
...>   {:ok, result} -> IO.puts "Handling result..."
...>   {:error} -> IO.puts "An error has occurred!"
...> end

iex> some_result = 1
iex> handle_result.({:ok, some_result})
Handling result...

iex> handle_result.({:error})
An error has occurred!

命名函数

命名函数通过 def关键字定义在某个模块中。

定义在模块内部的函数可以被其他模块使用,这在 Elixir 中构建代码块非常有用:

defmodule Greeter do
  def hello(name) do
    "Hello, " <> name
  end
end

iex> Greeter.hello("Sean")
"Hello, Sean"

如果我们的函数体只有一行,我们可以缩写成 do:

defmodule Greeter do
  def hello(name), do: "Hello, " <> name
end

学到了那么多模式匹配的知识,现在我们用命名函数实现递归

defmodule Length do
  def of([]), do: 0
  def of([_ | tail]), do: 1 + of(tail)
end

iex> Length.of []
0
iex> Length.of [1, 2, 3]
3

函数的名字和元数

函数的名称方式由名字和元数组成,你可以这样做。

defmodule Greeter2 do
  def hello(), do: "Hello, anonymous person!"   # hello/0
  def hello(name), do: "Hello, " <> name        # hello/1
  def hello(name1, name2), do: "Hello, #{name1} and #{name2}"
                                                # hello/2
end

iex> Greeter2.hello()
"Hello, anonymous person!"
iex> Greeter2.hello("Fred")
"Hello, Fred"
iex> Greeter2.hello("Fred", "Jane")
"Hello, Fred and Jane"

我们在上面代码注释中列出了函数的全称。第一个函数不接受任何参数,因此是 hello/0;第二个函数接受一个参数,因此是 hello/1,以此类推。不同于其他语言的函数重载,这些函数被认为是不同的。(刚刚提到过的模式匹配,只有当函数名字和接受的参数个数都匹配的时候才成立。)

私有函数

如果我们不想其他模块使用这个函数,我们可以使用私有函数,页就是只能被他所在模块调用的函数。在 Elixir 中,我们使用 defp, 来定义私有函数

defmodule Greeter do
  def hello(name), do: phrase <> name
  defp phrase, do: "Hello, "
end

iex> Greeter.hello("Sean")
"Hello, Sean"

iex> Greeter.phrase
** (UndefinedFunctionError) function Greeter.phrase/0 is undefined or private
    Greeter.phrase()

卫兵

在控制语句那提过卫兵,现在我们就来看看怎么在命名函数中使用它们。当 Elixir 匹配某个函数之后,后面的卫兵都会被检测。

在下面的例子中,我们定义了两个有相同签名的函数,而依赖判断参数类型的卫兵来确定调用那个函数。

defmodule Greeter do
  def hello(names) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello
  end

  def hello(name) when is_binary(name) do
    phrase() <> name
  end

  defp phrase, do: "Hello, "
end

iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

参数默认值

如果想给参数设置默认值,我们可以用argument \\ value语法:

defmodule Greeter do
  def hello(name, language_code \\ "en") do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello("Sean", "en")
"Hello, Sean"

iex> Greeter.hello("Sean")
"Hello, Sean"

iex> Greeter.hello("Sean", "es")
"Hola, Sean"

当我们同时使用卫兵和默认参数值的时候,会遇到问题,先看一下程序会报什么错:

defmodule Greeter do
  def hello(names, language_code \\ "en") when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello(language_code)
  end

  def hello(name, language_code \\ "en") when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

** (CompileError) iex:31: definitions with multiple clauses and default values require a header. Instead of:

    def foo(:first_clause, b \\ :default) do ... end
    def foo(:second_clause, b) do ... end

one should write:

    def foo(a, b \\ :default)
    def foo(:first_clause, b) do ... end
    def foo(:second_clause, b) do ... end

def hello/2 has multiple clauses and defines defaults in one or more clauses
    iex:31: (module)

Elixir 在处理多个匹配函数的时候,不喜欢默认参数这种模式,因为它很容易让人混淆。要处理这种情况,我们可以添加一个设置了默认参数值的函数头部:

defmodule Greeter do
  def hello(names, language_code \\ "en")

  def hello(names, language_code) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello(language_code)
  end

  def hello(name, language_code) when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

iex> Greeter.hello ["Sean", "Steve"], "es"
"Hola, Sean, Steve"

相关文章

  • Elixir-函数

    目录 匿名函数& 操作符 模式匹配 命名函数函数名字和元数私有函数卫兵参数默认值 匿名函数 匿名函数就是没有名字,...

  • Elixir-组合

    模块 模块是把函数组织到不同命名空间的最好的办法,除了能为函数分组,他还允许我们定义命名函数和私有函数。 Elix...

  • Elixir-推导

    在 Elixir 中,列表推导是循环遍历枚举值的语法糖。 基础 推导经常用来根据 Enum 和 Stream生...

  • Elixir-并发

    Elixir 的一大卖点就是对并发的支持,得益于 Erlang VM (BEAM) ,Elixir 的并发要比预期...

  • Elixir-基础

    安装 在https://elixir-lang.org 上可以找到安装说明。 使用 elixir -v 查看 el...

  • Elixir-集合

    列表、元组、关键字列表(keywords)、图(maps)、字典和函数组合子(combinators) 目录 列表...

  • Elixir-魔符

    Elixir 提供了一种叫做 魔符的语法糖来标识和处理字面量。一个魔符已~开头然后接上一个字符。Elixir 已经...

  • Elixir-模式匹配

    模式匹配是 Elixir 很强大的特性,它允许我们匹配简单值、数据结构、甚至函数。 匹配操作符 Elixir 中,...

  • Elixir-控制语句

    if 和 unless 你之前可能遇到过 if/2了,如果你使用过 Ruby,也会很熟悉 unless。它们在 E...

  • Elixir-文档模块

    Elixir提供了多种方式来编写注释或者是注解。下面是其中三种方式: # - 用于单行的注释 @moduledoc...

网友评论

      本文标题:Elixir-函数

      本文链接:https://www.haomeiwen.com/subject/lhqqmftx.html