尝试使用管道但不确定如何提取先前的值

时间:2018-12-06 16:05:22

标签: elixir

说我目前有这样的东西:

session = conn.assigns[:session]
user = User.find_by_id(session.user_id)

如何在此场景中使用|> sytle?

conn.assigns[:session]
|>User.find_by_id

如何传递先前的呼叫并从中获取属性user_id?

我还看到人们使用|>,并且我认为他们也在指定Arity,这是如何工作的?

some_code
|> some_function(&/2)

类似的事情,我不记得了,但是看着它我很困惑。

4 个答案:

答案 0 :(得分:2)

我认为您可能最好在函数头上进行模式匹配,并从那里的赋值中提取user_id,说过您可以使用Map.fetch!/2,如下所示:

conn.assigns[:session]
|> Map.fetch!(:user_id)
|> User.find_by_id

但是,正如您看到的那样,当您从映射转到单个值到架构结果时,管道的含义变得有些混乱。 IMO不会提高可读性或意图。

您也可以使用conn.assigns.session.user_id |> User.find_by_id

如果缺少任何中间字段,所有这些操作都会失败,但是如果您在功能头上进行模式匹配(我假设它在控制器中),则可以设置后备操作或捕获所有返回错误的操作。

答案 1 :(得分:2)

第一部分-您可以像这样使用管道:

conn.assigns[:session]
|> Map.get(:user_id) # Use function from Map module.
|> User.find_by_id()

第二部分。您看到Capture operator

您可以将其与需要匿名函数作为参数的函数一起使用(它是模块的本地函数或远程函数)

让我们以Ecto.Changeset.validate_change/3

为例

因此,第三个参数需要一个匿名函数(验证器),该函数具有2个参数,您可以在模式中编写一个私有函数,以在传递给变更集并使用它时验证更改:

defp validate_title(field, title) do
  if title == "foo" do
    [{field, "cannot be foo"}]
  else
    []
  end
end

因此,当您使用更改集验证它时,您可以执行以下操作:

changeset = change(%Post{}, %{title: "foo"})
changeset = validate_change(changeset, :title, &validate_title/2)

答案 2 :(得分:2)

  

在这种情况下如何使用|>风格?

有数十种不同的方法。正确的答案是在这种情况下您不使用管道运算符。因此,它被称为管道运算符。它打算用于管道

Elixir核心团队认为在声明中使用单个|>是反模式。您应该改用

user = User.find_by_id(conn.assigns[:session].user_id)

另一方面,如果您确实想在此处使用|>,则可以使用Access行为:

user =
  conn.assigns
  |> Map.get(:session)
  |> Map.get(:user_id)
  |> User.find_by_id()

答案 3 :(得分:1)

|>取其上方(或其左侧)的内容,并将该表达式的值作为第一个参数传递给您在管道右侧调用的函数。这就是管道的全部工作。因此,您尝试在此处使用管道:

conn.assigns[:session]
|>User.find_by_id

等效于:

User.find_by_id(
    conn.assigns[:session]
) 

这是另一个例子:

defmodule My do
  def package({x, y}) do
    %{width: x, length: y}
  end

  def modify(map, z) do
    Map.put_new(map, :height, z)
  end 

  def go(tuple) do
    tuple
    |> package()
    |> modify(10)
  end
end

在iex中:

~/elixir_programs$ iex c.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.go {1, 2}
%{height: 10, length: 2, width: 1}

My.go()定义中,为tuple参数变量分配了参数{1, 2},从而获得了以下管道:

{1, 2}
|> package()
|> modify(10)

第一个管道导致package()被第一个参数{1, 2}调用,因此package({1, 2})会返回

%{width: 1, length: 2}

留下您:

%{width: 1, length: 2}
|> modify(10)

然后管道导致调用modify(),第一个参数为%{width: 1, length: 2},因此modify(%{width: 1, length: 2}, 10),返回:

%{height: 10, length: 2, width: 1}
  

我也看到人们使用|>,我认为他们也在指定   阿里,这是怎么工作的?

    some_code
    |> some_function(&/2)

&用于几种不同的事物。首先,&将根据您指定的表达式创建一个匿名函数:

my_func = &(3*4)

这将创建匿名函数:

my_func = fun -> 3*4 end

嗯,不完全是。这实际上是行不通的,因为&要求&之后的表达式至少使用一个函数参数变量。功能参数变量?这是&的第二种用法。匿名函数的(未指定)参数变量可以在表达式中使用名称&1,&2,&3等进行引用。所以你必须写:

my_func = &(3 * &1)

这将创建匿名函数:

fn x -> 3 * x end

匿名函数具有Arity 1,因为在表达式中仅提及&1。如果在表达式中提到&1&2,则创建的函数的对数为2:

iex(5)> my_func = &(IO.puts "#{&2} ... #{&1}")
#Function<12.99386804/2 in :erl_eval.expr/5>

iex(6)> my_func.("hello", "world")
world ... hello

如果您在表达式中提到&1&3,而省略了&2,则会出现错误消息:

  

没有&2不能定义捕获&3。

您还可以使用&创建包装现有功能的匿名功能:

len = &Enum.count/1
len.([7,8,9])
=> 3

&创建匿名函数:

fun x -> Enum.count(x) end

要求您通过指定现有功能的名称和名称来唯一标识其功能。对于本地函数或导入的函数,只需编写&func_name/2并消除模块名称即可。