Elixir语法-定义具有相似签名且没有end关键字的多个函数

时间:2018-10-25 20:11:10

标签: elixir

我正在从以下位置签出示例应用程序:https://github.com/shamshirz/scoreboard/blob/master/lib/scoreboard/games/games.ex

我遇到了以下代码,这些代码是根据我的理解进行标记的。

def query(Score, params) do #defined function query/2
    params
    |> Map.put_new(:limit, 10) #adding to the "dictionary"
    |> Map.put_new(:order, :total) #""
    |> Map.to_list() #flattening out the map
    |> Enum.reduce(Score, &apply_param/2) #call apply_param/2 for each item
  end

#defines query/2... now we have two query/2... Wait, how is this?
#I guess this one is query/2 with a private argument (_params). 
#Also, this doesnt have an `end`
  def query(queryable, _params), do: queryable 

#defines apply_param/2 with no end
  def apply_param({:limit, num}, queryable), do: queryable |> limit(^num) 

#defines another apply_param/2, no end again!
  def apply_param({:order, field}, queryable), do: queryable |> order_by(desc: ^field)

#defines another apply_param/2, no end again!...
  def apply_param({:player_id, player_id}, queryable),
    do: queryable |> where(player_id: ^player_id)

#again...
  def apply_param(_param, queryable), do: queryable

#finally a function I can read (I think)
#take in a query and execute, if its null -> error, result -> return with :ok
  def get(queryable, id) do
    case Repo.get(queryable, id) do
      nil ->
        {:error, :not_found}

      result ->
        {:ok, result}
    end
  end
  #look at those nice "end"-ings

这些apply_param的定义是什么?他们为什么没有end?他们怎么都拥有看似相似的签名?即他们使用tuple和第二个queryable变量?

2 个答案:

答案 0 :(得分:2)

单行功能

这是编写单行丹参函数的捷径。例如,您可以编写以下函数:

def add(x, y) do
  x + y
end

赞:

def add(x, y), do: x + y

通知do:而不是do


模式匹配

代码使用的另一件事是函数子句中的模式匹配。它使开发人员可以避免大量的条件语句进行决策,而让编译器选择要调用的函数定义。

例如,请参见:

def something(num, :inc), do: num + 1
def something(num, :dec), do: num - 1

一旦定义,something/2函数的这两个实例将以不同的方式处理参数。因此,如果您调用something(4, :inc),它将与第一个定义“匹配”并返回5,如果您传递:dec,则它将返回3,但是如果第二个参数是其他的东西会抛出UndefinedFunctionClauseError

答案 1 :(得分:2)

如果使用单行功能,则不需要end关键字。要实现单行功能,请在前面的命令和后缀冒号中使用do。以下功能相同:

# Single line
def say_hello(name), do: IO.puts("Hello #{name}")

# Multiline
def say_hello(name) do
  IO.puts("Hello #{name}")
end

对于您的另一个问题,apply_param/2的所有定义都是模式匹配的示例。每个函数在不同的参数上匹配,这些参数将在第一个函数的reduce语句中应用。

参数列表可能如下所示:

[{:limit, 5}, {:order, :asc}]

所以reduce调用将被这样调用:

Enum.reduce([{:limit, 5}, {:order, :asc}], score,  &apply_param/2)

每个参数都将应用于得分,为reduce函数的下一次迭代返回修改后的得分。

{:limit, 5}参数将匹配第一个函数,而{:order, :asc}参数将不匹配第一个函数,但是将匹配第二个函数。

def apply_param({:limit, num}, queryable), do: queryable |> limit(^num) 

def apply_param({:order, field}, queryable), do: queryable |> order_by(desc: ^field)

def apply_param({:player_id, player_id}, queryable),
    do: queryable |> where(player_id: ^player_id)

def apply_param(_param, queryable), do: queryable

在最终功能中,_param中的下划线表示它将匹配任何内容。通常这是一个好主意:对未知输入使用默认大小写。


以下是我经常做的事情的例子,展示了这两个方面:

# This function declaration without a body is called a header, it is used so # I don't have define the default value of 5 in multiple places
def fetch_data(id, retries_left \\ 5)

# In this case, where the retries_left matches on the value of 0, I want to
# raise an error.  Since this is small, I use a one-line function
def fetch_data(id, 0), do: raise "Retries exhausted, could not fetch data!"

# In any other case, try getting the data again
def fetch_data(id, retries_left) do
  case do_request(id)
    # It didn't work, decrement and try again, this will eventually get us to 0
    {:error, _} -> fetch_data(id, retries_left - 1)

    # It works!  Return the payload
    {:ok, payload} -> payload
  end
end