我正在从以下位置签出示例应用程序: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
变量?
答案 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