我搜索过Elixir和Phoenix文档,以及其他一些网站,例如Learn Elixir但没有运气。这是它的样子:
defp update_positions(item_ids) do
item_ids = String.split(item_ids, ",")
|> Enum.map fn item_id -> String.to_integer(item_id) end
items = Repo.all(Item |> where([item], item.id in array(^item_ids, :integer)))
item_hash = Enum.reduce items, %{}, fn item, map -> Map.put(map, item.id, item) end
item_ids
|> Stream.with_index
|> Enum.each fn {item_id, index} ->
item = item_hash[item_id]
Repo.update(%{item | position: index + 1})
end
end
起初我认为它只是一个行继续符号来保持代码可读,但上面的Item |> where
行暗示了其他方面。它是列表推导还是指定输入类型的东西?
答案 0 :(得分:27)
我将从我的Elixir Express研讨会材料中复制: https://github.com/chrismccord/elixir_express/blob/master/basics/06_pipeline_operator.md
Elixir中最简单但最有效的功能之一是管道运算符。管道运算符解决了许多函数式语言在编写一系列转换时所面临的问题,其中一个函数的输出需要作为输入传递给另一个函数。这需要反向读取解决方案以理解正在执行的操作,妨碍可读性并模糊代码的真实意图。 Elixir通过允许函数的输出管道作为另一个输入的第一个参数,优雅地解决了这个问题。在编译时,函数层次结构将转换为嵌套的“向后”变体,否则将是必需的。
iex(1)> "Hello" |> IO.puts
Hello
:ok
iex(2)> [3, 6, 9] |> Enum.map(fn x -> x * 2 end) |> Enum.at(2)
18
要掌握管道提供的完整实用程序,请考虑从API获取新消息并将结果保存到数据库的模块。步骤顺序如下:
没有管道:
defmodule MessageService do
...
def import_new_messages(user_token) do
Enum.each(
parse_json_to_message_list(
fetch(find_user_by_token(user_token), "/messages/unread")
), &save_message(&1))
end
...
end
正确的命名和缩进有助于前一个块的可读性,但如果没有先花一点时间从内到外分解步骤来掌握对数据流的理解,它的意图就不会立即明显。
现在考虑使用管道运算符的这一系列步骤:
使用Pipeline
defmodule MessageService do
...
def import_new_messages(user_token) do
user_token
|> find_user_by_token
|> fetch("/messages/unread")
|> parse_json_to_message_list
|> Enum.each(&save_message(&1))
end
...
end
将每个步骤的结果作为下一个允许的第一个参数的管道允许程序被编写为一系列转换,任何读者都可以立即阅读和理解这些转换,而无需花费额外的精力来打开函数,如第一解决方案
Elixir标准库侧重于将函数的主题作为第一个参数,帮助和鼓励管道的自然使用。