【问题标题】:Ruby: why this WHEN-statement doesn't work with two conditions?Ruby:为什么这个 WHEN 语句不适用于两个条件?
【发布时间】:2019-04-08 18:46:15
【问题描述】:

我的 Ruby 代码中有一个 case,它基本上尝试以几种方式匹配字符串并相应地对其执行不同的操作,有点像这样:

case inst
when Rx_inst1
  (some function)
when Rx_inst2
  (some other function)
end

这很好用(我没有发布正则表达式,因为它非常非常长并且效果很好)。但是,当我尝试实现以下修改时,即使 RegEx 匹配字符串,如果它包含子字符串,则不会执行条件:

case inst
when Rx_inst1
  (some function)
when Rx_inst2 && !/dom\./
  (some other function)
end

当它等于Nm. Friese = Fryzyjczyk, koń fryzyjski 或它之前匹配的任何其他类型的字符串时,它Rx_inst2 停止匹配inst

我得到了与&& /dom\./&& inst.include?('dom.') 完全相同的结果,根据其他答案,这是正确的语法。我做错了什么?

【问题讨论】:

    标签: ruby conditional operators


    【解决方案1】:
    case inst
    when Rx_inst1
      (some function)
    when Rx_inst2 && !/dom\./
      (some other function)
    end
    

    case 转换为带有 .=== 运算符的 if/elsif/else 语句 所以你的代码相当于

    if Rx_inst1 === inst
      (some function)
    elsif (Rx_inst2 && !/dom\./) === inst
      (some other function)
    end
    

    (Rx_inst2 && !/dom\./) === inst 的工作方式如下:

    1. (Rx_inst2 && !/dom\./) 部分的计算结果为 !/dom\./(Rx_inst2 是一个正则表达式,不是 nil 或 false)
    2. !/dom\./ === inst 给你结果

    所以 when Rx_inst2 && !/dom\./!/dom\./ === inst 匹配时匹配

    【讨论】:

    • 哦,所以返回 nil 的 RegEx 本身不等于 nil 吗?有趣的。为什么我的最终解决方案(我的答案中的那个)起作用了?是不是因为它首先将表达式评估为真/假,然后尝试将 RegEx 匹配为匹配或空匹配?
    • @MrVocabulary 不,这是因为!inst.include?('dom.') 要么返回true 要么返回false。如果false,所有这些&& 表达式都返回false 并且false === inst 也返回false(除非instfalse 本身,但它不能在这里发生,因为它会更早崩溃)。如果true&&表达式返回第二个操作数,即Rx_inst2Rx_inst2 === inst比较,返回正确结果。
    【解决方案2】:

    很明显,Rx_inst2 && !/dom\./ 返回(由于短路评估)第二个操作数的值,即!/dom\/(前提是Rx_inst2 确实是Regexp,因此是真实的)。您可能需要在Rx_inst2 中修改此正则表达式,没有简短的答案。

    或者,您可以稍微更改所有 case 表达式,如下所示:

    case
    when inst =~ Rx_inst1
      # (some function)
    when inst =~ Rx_inst2 && !(inst =~ /dom\./)
      # (some other function)
    end
    

    但在我看来,它并不是特别优雅的形式。

    【讨论】:

    • 能否请您详细说明一下为什么这里短路评估很明显?
    • 好吧,将Rx_inst2 && !/dom\./ 视为一个单独的表达式(确实如此)。它如何评价?顺便说一句,在 Ruby 中,所有这些运算符:&&, ||, and, or 使用短路评估。
    • 两者都分别返回true,所以当两个元素都是true时,整体不应该是true吗?
    • 不,首先,在 Ruby 中,二元逻辑运算符不只返回布尔值。我编辑了我的答案,以提供一些 hacky 的替代方法来做你想做的事情。
    • @MrVocabulary 哦,对不起,我在我的 hacky 解决方案代码中犯了一个错误,已修复它,现在它应该可以按您的意愿工作并且更具可读性。
    【解决方案3】:

    @Darigazz 很好地解释了case 的工作原理以及您的代码有问题的原因。您可以通过以下几种方式编写案例陈述。

    #1

    case inst
    when Rx_inst1
      m1  
    when Rx_inst2
      m2 if inst.match? /dom\./
    end
    

    如果inst.match? /dom\./ #=> falsem2 if inst.match? /dom\./ 中,则返回nil。根据要求,if 可能会替换为 unless

    #2

    case inst
    when Rx_inst1
      m1  
    when /(?=.*dom\.)#{Rx_inst2}/
      m2
    end
    

    其中(?=.*dom\.)正向预测。例如,

    Rx_inst2 = /Bubba|Trixie/
    /(?=.*dom\.)#{Rx_inst2}/
       #=> /(?=.*dom\.)(?-mix:Bubba|Trixie)/
    

    根据要求,(?=.*dom\.) 可能会替换为 负前瞻 (?!.*dom\.)

    【讨论】:

    • 谢谢你。我正在尝试学习生成更紧凑的代码,因此避免嵌套过多的 if。另外,由于子字符串位于匹配的字符串内,因此前瞻不起作用,我没有明确说明,但我一直的意图是 ;-)
    • 请详细说明您评论的最后一句话(并更正“开始”)。
    • 声明*。我首先检查字符串是否符合我的正则表达式——它很大,而且是不需要的“dom”。几乎可以在任何地方。插入lookahead/lookbehinds会很笨拙并且会给CPU带来很多压力。在我的情况下,使用包含插入的修改正则表达式更有意义。
    【解决方案4】:

    好的,结果证明我是这样工作的:

    when (!inst.include?('dom.') and Rx_inst2)

    我仍然不明白为什么我必须做以下所有这三件事:

    1. 更改操作顺序
    2. 将整个表达式括起来
    3. 显式调用第一个,隐式调用第二个

    【讨论】:

    • 不要使用and,这很混乱,请改用&&。另外,它比我的方式更 hacky(它依赖于 === false 在底层的比较),所以如果你确实应用了这个解决方案,我有点同情稍后可能会看到这段代码的开发人员。
    • @Marek Lipka 对我来说更具可读性。除了操作中的优先级,比如!not,它真的改变了什么吗?
    • @Marek Lipka 可能没有其他人会看到该代码,而且它处理的案例范围很窄。我会保留它,除非向我展示一个单线来做同样的事情,并且需要更好的练习。不过,感谢您的建议,让我参加一些关于运营商的有趣讲座:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-28
    • 2016-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多