具有多个变量的 Ruby case 语句

发布于 2024-10-15 03:02:53 字数 286 浏览 3 评论 0原文

当您需要针对单个变量匹配条件时,Ruby 有一个相当强大的 case..when..else 构造。在不简单嵌套 case 语句的情况下将条件与多个变量进行匹配的“规范”方法是什么?

将多个变量包装在一个数组中(例如 [x, y])并对其进行匹配并不等效,因为 Ruby 不会应用神奇的 case === 运算符到数组的元素;该运算符仅应用于数组本身。

我将继续用社区维基的答案来回应这个问题(失败的)。

Ruby has a fairly powerful case..when..else construct for when you need to match criteria against a single variable. What is the "canonical" way to match criteria against multiple variables without simply nesting case statements?

Wrapping multiple variables in an array (like [x, y]) and matching against it isn't equivalent, because Ruby won't apply the magical case === operator to the elements of the array; the operator is only applied to the array itself.

I'm going to go ahead and respond with a community-wiki answer with a (defeated) stab at this question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

小鸟爱天空丶 2024-10-22 03:02:53

您需要使用 if..elsif..else,并确保要匹配的变量出现在 === 运算符的右侧(这就是 case 本质上所做的)。

例如,如果您想根据某些条件匹配 xy

if (SomeType === x) && (1..10 === y)
  some_value
elsif (:some_symbol === x) && (11..20 === y)
  some_other_value
end

You need to use an if..elsif..else, and ensure that the variables you want to match against appear on the right-hand side of the === operator (which is what case essentially does).

For example, if you want to match x and y against some criteria:

if (SomeType === x) && (1..10 === y)
  some_value
elsif (:some_symbol === x) && (11..20 === y)
  some_other_value
end
你的背包 2024-10-22 03:02:53

这是添加 === 的简单方法:

class Array
  def ===(other)
    return false if (other.size != self.size)

    other_dup = other.dup
    all? do |e|
      e === other_dup.shift
    end
  end
end

[
  ['foo', 3],
  %w[ foo bar ],
  %w[ one ],
  []
].each do |ary|

  ary_type = case ary
  when [String, Fixnum] then "[String, Fixnum]"
  when [String, String] then "[String, String]"
  when [String] then "[String]"
  else
    "no match"
  end

  puts ary_type

end

# >> [String, Fixnum]
# >> [String, String]
# >> [String]
# >> no match

This is a simplistic way to add ===:

class Array
  def ===(other)
    return false if (other.size != self.size)

    other_dup = other.dup
    all? do |e|
      e === other_dup.shift
    end
  end
end

[
  ['foo', 3],
  %w[ foo bar ],
  %w[ one ],
  []
].each do |ary|

  ary_type = case ary
  when [String, Fixnum] then "[String, Fixnum]"
  when [String, String] then "[String, String]"
  when [String] then "[String]"
  else
    "no match"
  end

  puts ary_type

end

# >> [String, Fixnum]
# >> [String, String]
# >> [String]
# >> no match
来世叙缘 2024-10-22 03:02:53

如果这种模式在您的代码中足够常见以保证经济的表达,您可以自己做:

class BiPartite
  attr_reader :x, :y

  def self.[](x, y)
    BiPartite.new(x, y)
  end

  def initialize(x, y)
    @x, @y = x, y
  end

  def ===(other)
    x === other.x && y === other.y
  end
end

....

case BiPartite[x, y]
when BiPartite[SomeType, 1..10]
  puts "some_value"
when BiPartite[:some_symbol, 11..20]
  puts "some_other_value"
end

If this pattern is common enough in your code to warrant economical expression, you can do it yourself:

class BiPartite
  attr_reader :x, :y

  def self.[](x, y)
    BiPartite.new(x, y)
  end

  def initialize(x, y)
    @x, @y = x, y
  end

  def ===(other)
    x === other.x && y === other.y
  end
end

....

case BiPartite[x, y]
when BiPartite[SomeType, 1..10]
  puts "some_value"
when BiPartite[:some_symbol, 11..20]
  puts "some_other_value"
end
心凉怎暖 2024-10-22 03:02:53

由于 Ruby 的 when 关键字支持逗号分隔的值列表,因此您可以使用 splat * 运算符。当然,这是假设您引用的是数组中或可能成为数组的一组离散值。

splat 运算符将参数列表转换为数组,正如在

def method_missing(method, *args, &block)

不太为人所知的事实中经常看到的那样,它还执行逆操作 - 将数组转换为参数列表。

所以在这种情况下,你可以这样做

passing_grades = ['b','c']
case grade
when 'a'
  puts 'great job!'
when *passing_grades
  puts 'you passed'
else
  puts 'you failed'
end

Since Ruby's when keyword supports a comma separated list of values, you could use the splat * operator. This is, of course, assuming that you're referring to a set of discrete values that are in or could become an array.

The splat operator converts a list of arguments into an array, as frequently seen in

def method_missing(method, *args, &block)

Less well known is the fact that it also performs the inverse operation - turning an array into a list of arguments.

So in this case, you could do something like

passing_grades = ['b','c']
case grade
when 'a'
  puts 'great job!'
when *passing_grades
  puts 'you passed'
else
  puts 'you failed'
end
执妄 2024-10-22 03:02:53

我未经测试的解决方案是调用 .map(&:class) ,然后是 when 语句中的数组

def foo(a, b)
  case [a,b].map(&:class)
  when [Integer, Integer]
    puts "Integer"
  when [String, String]
    puts "String"
  else 
    puts "otherwise"
  end
end

My not battled tested solution is to call .map(&:class) and then is the array in the when statements

def foo(a, b)
  case [a,b].map(&:class)
  when [Integer, Integer]
    puts "Integer"
  when [String, String]
    puts "String"
  else 
    puts "otherwise"
  end
end
海螺姑娘 2024-10-22 03:02:53

如果您正在使用字符串或可以轻松转换为字符串的内容,您可能会喜欢以下解决方案:

case [city, country].join(", ")
when "San Jose, Costa Rica"
  # ...
when "San Jose, USA"
  # ...
when "Boston, USA"
  # ...
else
  # ...
end

If you're working with strings, or something that converts to strings easily, you might like this solution:

case [city, country].join(", ")
when "San Jose, Costa Rica"
  # ...
when "San Jose, USA"
  # ...
when "Boston, USA"
  # ...
else
  # ...
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文