【问题标题】:Why is the string '3' not matched in a case statement with the range ('0'...'10')?为什么字符串 '3' 在范围 ('0'...'10') 的 case 语句中不匹配?
【发布时间】:2021-04-23 17:12:28
【问题描述】:

当我尝试在 case 语句中匹配字符串“3”时,如果范围达到“9”而不是“10”,则匹配。

我猜它与三等号运算符有关,但我不知道它可以在范围内但不匹配的确切原因。

这是一个 IRB 运行,记录了两种情况有效(使用“9”)和不有效(使用“10”):

 case '3'
 when ('0'...'9')
     puts "number is valid"
 else
   puts "number is not valid"
 end

输出:number is valid

 case '3'
 when ('0'...'10')
     puts "number is valid"
 else
   puts "number is not valid"
 end

输出:number is not valid

我用作预期结果参考的方法是
Enumerable#include?
Enumerable#member?
并查看转换为数组时的输出是 (Enumerable#to_a)。

“大小写相等”(===) 运算符的结果让我吃惊。

 puts ('0'...'10').include?('3')
 # => true
 puts ('0'...'10').member?('3')
 # => true
 puts ('0'...'10') === '3'
 # => false
 puts ('0'...'10').to_a
 # => ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

【问题讨论】:

  • TL;DR:字典顺序与字符串转换为数字顺序不同。
  • 如果你的范围不是那么大,你可以使用when *'0'...'10',它将你的范围变成一个参数列表,即when '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
  • @Stefan 实际上我最喜欢这个解决方案,因为我正在使用它。简明扼要,只要下一个人理解 splat 运算符的作用,以后应该会更容易阅读。我问了一个错误的问题来作为答案,但这是迄今为止我的问题的最佳解决方案。谢谢!

标签: ruby


【解决方案1】:

范围使用cover? 代替case equality。所以它比较了'3' >= '0' && '3' < '10',结果是false,因为'3' < '10' #=> false。字符串根据字符值进行比较。

为了更好地理解,您可能希望将字符串视为字符数组:

['3'] <=> ['1', '0'] #=> 1 (first operand is larger than the second)

要解决此问题,请将您的 case 输入转换为整数并使用整数范围:

case 3 # or variable.to_i
when 0...10
  puts 'number is valid'
else
  puts 'number is invalid'
end

之所以有效,是因为整数不是根据字符代码进行比较,而是根据实际值进行比较。 3 &gt;= 0 &amp;&amp; 3 &lt; 10 导致 true

或者,您可以明确告诉何时使用member?(或include?)方法,而不是传递一个范围,而是一个方法。

case '3'
when ('0'...'10').method(:member?)
  puts 'number is valid'
else
  puts 'number is invalid'
end

【讨论】:

  • 感谢您提供解决方案以及回答问题。传入的数据需要是一个字符串,因为列表中有其他 when 匹配,所以包括 .method(:member?) 选项实际上是解决我的问题的方法。我需要更深入地了解为什么会这样,但这是一个可行的解决方法!
  • @jockofcode case equality of a method 使用 case 参数/输入调用方法。因此,将 range.method(:member?) 传递给 when 使用 range.method(:member?) === input -> range.method(:member?).call(input) 解决它,而仅提供 range 将导致 range === input -> range.cover?(input)
  • @jockofcode Method#===: “以obj为参数调用方法。这允许方法对象成为case语句中when子句的目标。”我>
  • @jockofcode 在这个特定场景中,还可以选择使用'0'..'9'(两个点而不是三个点,请参阅Range 文档)。但这仅在您期望一个数字时才有效。这类似于正则表达式[0-9]
  • @3limin4t0r:我使用它的实现需要能够有一个空范围,为此我需要三个点。它适用于具有多种字母选项的文本菜单,还有一个数字选项,最多可查看 10 个搜索结果。如果搜索未返回任何结果,则 ('0'...[results.length,10].min.如果没有搜索结果,to_s) 将不匹配“0”。
【解决方案2】:

=== 表示它等同于cover?,后者的文档声明它等同于

begin <= obj < end

所以,在你的情况下,我们得到了

'0' <= '3' < '10'

字符串上的&lt;=&lt;使用字典顺序进行比较,所以比较结果为假。

另一方面,我们必须做更多的挖掘来弄清楚member? / include? 实际做了什么(两者是等价的)。如果我们查看源代码,我们会看到两者都调用了一个名为range_include_internal 的函数,该函数对字符串参数有一个特殊情况,其行为与cover? 不同。后者调用rb_str_include_range_p,它有更多特殊情况,包括您的数字情况。

【讨论】:

    猜你喜欢
    • 2023-02-20
    • 1970-01-01
    • 2020-10-14
    • 1970-01-01
    • 1970-01-01
    • 2017-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多