Ruby case..when.. 条件是 class 的时候不能理解的现象

shawnyu · 2013年12月10日 · 最后由 lg2046 回复于 2013年12月19日 · 3943 次阅读
ruby v
=> 2.0.0-p247

a = [1,2]

a.class
=> Array

a.class == Array
=> true

case a.class
when Array
  puts 1
else
  puts 2
end

=> 2

这个怎么理解,或者说其中原理是什么? 刚被坑了一下。

所以最后我觉得 case..when..应该这么写吧

case a
when Array
  puts 1
else
  puts 2
end

=> 1

下面是从 Stack Overflow 上扒下来的

I'm going to heavily quote the Object documentation here, because I think it has some great explanations. I encourage you to read it, and also the documentation for these methods as they're overridden in other classes, like String.

Side note: if you want to try these out for yourself on different objects, use something like this:

class Object
  def all_equals(o)
    ops = [:==, :===, :eql?, :equal?]
    Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })]
  end
end

"a".all_equals "a" # => {"=="=>true, "==="=>true, "eql?"=>true, "equal?"=>false}

== — generic "equality"

At the Object level, == returns true only if obj and other are the same object. Typically, this method is overridden in descendant classes to provide class-specific meaning.

This is the most common comparison, and thus the most fundamental place where you (as the author of a class) get to decide if two objects are "equal" or not.

=== — case equality

For class Object, effectively the same as calling #==, but typically overridden by descendants to provide meaningful semantics in case statements.

This is incredibly useful. Examples of things which have interesting === implementations:

Range Regex Proc (in Ruby 1.9)

So you can do things like:

case some_object
when /a regex/
  # The regex matches
when 2..4
  # some_object is in the range 2..4
when lambda {|x| some_crazy_custom_predicate }
  # the lambda returned true
end

See my answer here for a neat example of how case+Regex can make code a lot cleaner. And of course, by providing your own === implementation, you can get custom case semantics.

eql? — generic (possibly alternate) equality

The eql? method returns true if obj and other have the same value. Used by Hash to test members for equality. For objects of class Object, eql? is synonymous with ==. Subclasses normally continue this tradition, but there are exceptions. Numeric types, for example, perform type conversion across ==, but not across eql?, so:

1 == 1.0     #=> true
1.eql? 1.0   #=> false

So you're free to override this for your own uses, or you can just override == and by default eql? will behave the same way.

equal? — identity comparison

Unlike ==, the equal? method should never be overridden by subclasses: it is used to determine object identity (that is, a.equal?(b) iff a is the same object as b).

This is effectively pointer comparison.

case 的判断标准不是==,而是===,这样你就明白了

1.9.3p448 :001 > /a/ === 'abcd'
 => true 
1.9.3p448 :002 > String === 'a'
 => true 

同一楼。

PS:

a=[1,2]
Array===a
true
a===Array
false
a.class===Array
false

===在类型判断中用来判定有操作数是不是左操作数的实例。这样和一楼的整合起来你就理解了。

=== 可以理解为“被归入”,“被包括”(subsumption),详见这里:http://stackoverflow.com/questions/3422223/vs-in-ruby

之前一直错误的写过

case key
when "a"
when "b"
  do_foo
when "c"
  do_bar
end

结果发现这样写是不对的,得

case key
when "a","b"
  do_foo
when "c"
  do_bar
end

===基于 == range regex lambda kind_of 这四种重写了==

需要 登录 后方可回复, 如果你还没有账号请 注册新账号