首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >"with“运算符中的逻辑条件不起作用

"with“运算符中的逻辑条件不起作用
EN

Stack Overflow用户
提问于 2020-05-30 05:39:09
回答 3查看 84关注 0票数 0

我有这样的代码:

代码语言:javascript
复制
  def edit(conn, params) do
    with m1 <- Repo.get(Model1, params["model1_id"]),
      m2 <- Repo.get(Model2, params["model2_id"]),
      !is_nil(m1) and !is_nil(m2)
    do
      # 1
      res = !is_nil(m1) and !is_nil(m2)
      IO.puts("***** res: #{res}")                              # ===> false

      IO.puts("***** m1: #{Kernel.inspect(m1)}")                # ===> prints a struct
      IO.puts("***** m1 is_nil: #{is_nil(m1)}")                 # ===> false

      IO.puts("***** m2: #{Kernel.inspect(m2)}")                # ===> nil
      IO.puts("***** m2 is_nil: #{is_nil(m2)}")                 # ===> true

    else
      #2
      _ -> raise ArgumentError, "not found"
    end
  end

即使m2为零,流#1也会被执行。怎么可能呢?怎么修呢?目标-确保m1和m2不是零,然后执行流#1。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-05-30 07:10:43

The with

with表达式严格用于模式匹配。它不是if-else条件词的“链式替代”。

基本上,with将遍历所有子句,并尝试将它们与<-箭头的左侧匹配。当第一个模式匹配失败(不匹配)时,它将只执行一个错误子句。

代码的问题

with中的第三行是!is_nil(m1) and !is_nil(m2),它总是成功地匹配模式,即使表达式本身等于false

修复

要使代码执行您实际需要的操作,您应该在第三行中添加一个左侧,以便它被迫进行模式匹配:

代码语言:javascript
复制
with m1 <- Repo.get(Model1, params["model1_id"]),
      m2 <- Repo.get(Model2, params["model2_id"]),
      {false, false} <- {is_nil(m1), is_nil(m2)} do
 ...

惯用药

为了使代码更具有习惯性,您还可以使用守卫is_nil是允许的。这将使您的代码看起来像:

代码语言:javascript
复制
with m1 when not is_nil(m1) <- Repo.get(Model1, params["model1_id"]),
      m2 when not is_nil(m2) <- Repo.get(Model2, params["model2_id"]) do
 ...

可读性更好

最后一条建议是始终关注可读性。您正在编写供人阅读的代码,因此较少发生在行上的事情通常更容易阅读。

您的代码将更具可读性,如下所示:

代码语言:javascript
复制
m1 = Repo.get(Model1, params["model1_id"])
m2 = Repo.get(Model2, params["model2_id"])

with m1 when not is_nil(m1) <- m1,
      m2 when not is_nil(m2) <- m2 do
 ...

你真的需要一个with吗?

您的with除了确保m1m2不是nil之外,什么也不做。使用caseif也可以很容易地做到这一点,因为这里实际上不需要任何模式匹配:

代码语言:javascript
复制
m1 = Repo.get(Model1, params["model1_id"])
m2 = Repo.get(Model2, params["model2_id"])

if !is_nil(m1) && !is_nil(m2) do
 ...
票数 0
EN

Stack Overflow用户

发布于 2020-05-30 05:51:16

如果并且只有子句中没有匹配时,Kernel.SpecialtForms.with/1“早期返回”。

在第三个子句中,您有!is_nil(m1) and !is_nil(m2) (大致意思是_ <- !is_nil(m1) and !is_nil(m2) ),它无论如何都匹配。为了实现您想要的结果,您需要在with中显式地使用适当的<-子句

代码语言:javascript
复制
with m1 <- Repo.get(Model1, params["model1_id"]),
     m2 <- Repo.get(Model2, params["model2_id"]),
     true <- !is_nil(m1) and !is_nil(m2), do: ...

更自然的做法是使用适当的警卫来早期返回错误:

代码语言:javascript
复制
with m1 when not is_nil(m1) <- Repo.get(Model1, params["model1_id"]),
     m2 when not is_nil(m2) <- Repo.get(Model2, params["model2_id"]),
       do: ...

事实上,这里不需要with/1。这将非常完美(感谢nilfalsey):

代码语言:javascript
复制
if Repo.get(Model1, params["model1_id"]) &&
   Repo.get(Model2, params["model2_id"]), do: ...
票数 2
EN

Stack Overflow用户

发布于 2020-05-31 03:04:06

我发现我只在特定的情况下使用with,而在那些情况下,它确实很有帮助,主要是当它是一组类似于“管道”的操作时,就像在下一个步骤中一样,您需要前面步骤的结果,但实际上是异构的,您没有或者没有意义创建一些令牌结构来保存转换和错误(类似ecto )。

在这些情况下,如果知道需要执行失败的步骤,我发现在标记的元组上包装with语句会有所帮助,因为这样就可以匹配已失败的特定标记。此外,您并没有真正使用惯用代码,因为您使用的是with作为赋值表达式,如果使用模式匹配,那么它将变得更加可读性,在我看来,它更具有惯用性。就你的例子而言,这意味着:

代码语言:javascript
复制
with {_, %Model1{} = m1} <- {Model1, Repo.get(Model1, params["model1_id"])},
     {_, %Model2{} = m2} <- {Model2, Repo.get(Model2, params["model2_id"])}
       do
          # we have both m1 and m2 and they are respectively instances of Model1 and Model2
          # do something with them
          {:ok, {m1, m2}}
else
     {Model1, _} -> 
            #failed fetching Model1
            {:error, :no_model1}
     {Model2, _} ->
            #failed fetching Model2
            {:error, :no_model2}
end

对所需结构进行模式匹配,并且知道Repo.get将返回模式结构或零,因此不必检查它是否为零,如果不是模式结构,则为零(除非使用带有select子句的Repo.get来返回其他ofc)。

请记住,访问params["some_key"]可能会返回nil,这将在尝试执行Repo.get时引发异常,因此您可以添加两个带有id条件的语句,并且还可以返回id的条件,前提是id是数字id(如果二进制文件将is_integer更改为is_binary):

代码语言:javascript
复制
with {_, id1} when is_integer(id1) <- {:id1, Map.get(params, "model1_id")},
     {_, id2} when is_integer(id2) <- {:id2, Map.get(params, "model2_id")},
     {_, _, %Model1{} = m1} <- {Model1, id1, Repo.get(Model1, id1)},
     {_, _, %Model2{} = m2} <- {Model2, id2, Repo.get(Model2, id2)}
       do
          # we have both m1 and m2 and they are respectively instances of Model1 and Model2
          # do something with them
          {:ok, {m1, m2}}
else
     {id_type, id_value} when id_type in [:id1, :id2] ->
            # one of the id params wasn't an integer
            {:error, {:unexpected_id, id_type, id_value}}
     {Model1, id, _} -> 
            # failed fetching Model1
            {:error, {:no_model1, id}}
     {Model2, id, _} ->
            # failed fetching Model2
            {:error, {:no_model2, id}}
end

可能需要更好地处理这些参数,比如在执行到此阶段之前对它们进行验证,如果这样做了,您可能会使用case语句,因为它只有2种“情况”:

代码语言:javascript
复制
case Repo.get(Model1, valid_id_1) do
    %Model1{} = model1 ->

       case Repo.get(Model2, valid_id_2) do
           %Model2{} = model2 -> {:ok, {model1, model2}}
           nil -> {:error, {:no_model2, valid_id_2}}
       end

    nil -> {:error, {:no_model1, valid_id_1}}
end
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62098298

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档