【问题标题】:Elixir - do nothing under particular case conditionElixir - 在特定情况下什么也不做
【发布时间】:2017-04-27 21:53:28
【问题描述】:

在 Elixir 的 case 语句中,如果在 case 语句中满足特定条件,是否可以什么都不做?还是必须总是退回一些东西?

为了说明,这是我正在开发的 Phoenix 应用程序的 sn-p:

Enum.map(record_params, fn(record_id) ->
    record = Repo.get!(Record, record_id)
    case Repo.update(record) do
        {:ok, struct} ->
            # I DON'T REALLY NEED ANYTHING TO HAPPEN HERE... BUT I HAVE TO HAVE A CLAUSE TO MATCH WHEN THE UPDATE RETURNS {:ok, struct}
            IO.inspect struct
        {:error, changeset} ->
            errors = parse_errors(changeset)
            IO.inspect errors
            json(conn |> put_status(400), %{status: "error", message: "There was a problem updating this record.", errors: errors})
                end
end)

如果record 已更新,我需要知道是否有错误,获取有关它的信息并将其返回给客户端,因此需要case 语句......但我真的不如果记录已成功更新,则需要做任何事情 - {:ok, struct}。由于这些更新是在Enum.map() 内进行的,如果更新成功,我只希望地图继续循环通过record_ids

目前,我只是将IO.inspect struct 置于成功条件下 - 这是无害的,但并不是真正必要的。如果可能的话,我宁愿清理我的代码。由于 Elixir 的模式匹配,我无法删除 {:ok, struct} 条件,如果我在该条件下什么都不放,我会收到错误 syntax error before: '->'

现在我对 Elixir(以及一般的函数式编程范式)完全陌生,所以如果有更“Elixirish”的方式来处理这种情况,我很想听听。

【问题讨论】:

  • 这还能用吗?您可能在这里多次致电json。另外,当所有更新成功时,您如何处理响应?
  • 你是对的,这是有问题的。这个成功响应稍后会出现,在其他一些代码执行之后 - 我把它省略了,因为它看起来没有密切关系。

标签: elixir


【解决方案1】:

尽管已经有一堆答案,但我会发布另一个。 忽略除一个返回之外的所有返回的惯用 Elixir 方法是使用 Kernel.SpecialForms.with/1

with {:error, changeset} <- Repo.update(record) do
  # Do whatever you want here
end

do 块将在且仅匹配发生时执行,否则不匹配的 RHO 值({:ok, _} 将直接返回。)

有关执行您想要的正确代码,请参阅@Dogbert 的答案。

【讨论】:

  • 这是其他专门寻找此问题标题的人的正确答案。
【解决方案2】:

您的代码有误,因为您可能会在同一个conn 上多次调用json,以防出现多个Repo.update 错误。在这种情况下,您需要停止处理列表的其余部分。此外,由于您实际上并没有检测到何时出现任何错误,因此您可能稍后会在同一个 conn 上无条件地再次调用 json。如果您查看日志,您应该会看到 Phoenix 正在多次向同一个 conn 写入响应,例如

[debug] Processing by MyApp.PageController.index/2
  Parameters: %{}
  Pipelines: [:browser]
[info] Sent 200 in 130µs
[info] Sent 200 in 232µs
[info] Sent 200 in 498µs
[info] Sent 200 in 390µs
[info] Sent 200 in 182µs

这是我使用Enum.reduce_while/3 的方法:

errors = Enum.reduce_while(record_params, nil, fn(record_id, nil) ->
  record = Repo.get!(Record, record_id)
  case Repo.update(record) do
    {:ok, struct} ->
      {:cont, nil}
    {:error, changeset} ->
      errors = parse_errors(changeset)
      {:halt, errors}
  end
end)

if errors do
  json(conn |> put_status(400), %{status: "error", message: "There was a problem updating this record.", errors: errors})
else
  json(conn, "no errors!")
end

【讨论】:

  • 这真的很酷——你是对的,我的设置方式绝对不正确,可能会多次调用json。非常感谢,这真的很有帮助。
【解决方案3】:

Elixir 继承的 Erlang 约定是使用 :okIO.puts 的输出一样

没有分配开销返回一个原子,因为它们都被运行时拦截。

【讨论】:

  • 谢谢 - 这真的很有帮助。 +1
【解决方案4】:

每个 Elixir 函数都返回最后一个评估语句的值。如果您不想从一个无论如何都不会使用返回值的函数返回任何内容,您可以在不需要返回任何内容的情况下添加nil

Enum.map(record_params, fn(record_id) ->
    record = Repo.get!(Record, record_id)
    case Repo.update(record) do
        {:ok, struct} ->
            nil
        {:error, changeset} ->
            errors = parse_errors(changeset)
            IO.inspect errors
            json(conn |> put_status(400), %{status: "error", message: "There was a problem updating this record.", errors: errors})
                end
end)

【讨论】:

  • 这基本上是对的,但是IO.inspect/2 实际上返回了你传入的东西。
  • 非常感谢,这真的很有帮助。我实际上会接受关于使用if 语句的建议,因为它简洁地总结了我遇到的问题,但这是一个很好的答案。 +1
  • 很高兴为您提供帮助! :)
【解决方案5】:

在这种特殊情况下,因为您只想为一个分支做某事,您可以只使用if/2

{status, changeset} = Repo.update(record)
if status == :error do
  # Do whatever you want here
end

如果你需要匹配多个东西,你会想要使用 case 并为那个分支返回一些东西。在上述情况下,我会返回 :ok,因为操作成功。

【讨论】:

  • 这些都是很好的答案,但我选择这个是因为它确实是我特定场景的最佳解决方案。谢谢!我正在考虑使用if 语句,但不知道如何获取返回值。把结果放在左边就行了。
  • == 不会在这里工作,除非您出于某种原因已经拥有与 Repo.update 将返回的值相同的 changeset 值,而 = 也不会使用 @ 987654329@,因为如果模式不匹配,它将引发MatchError
  • 很好,您仍然需要将变更集分配给某些东西。让我快速编辑一下。
  • 除非Repo.update(record) 返回默认的变更集,否则仍然无法工作。
猜你喜欢
  • 2021-08-12
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多