为什么在这个case语句中Ruby类比较失败?

huangapple go评论70阅读模式
英文:

Why does ruby class comparison fail in this case statement?

问题

在给定的rspec示例中,为什么when Question不是真值?

require "rails_helper"
class Question
end

describe Question do
  it "confuses me" do
    klass = Question

    # this passes
    expect(klass).to eq(Question)

    # this doesn't fail (thankfully)
    fail "loudly" if klass != Question

    case klass
    when Question # this doesn't match 🤔???
      tada = "🎁"
      expect(tada).to eq("🎁")
    else
      # why?????
      fail "into confusion"
    end
  end
end

而且,不,这不是一项课程作业,哈哈。我在工作中遇到了这个问题,现在我不知道什么是真的了。

另外,使用字符串比较可以工作:

require "rails_helper"
class Question
end

describe Question do
  it "confuses me" do
    klass = Question

    # this passes
    expect(klass).to eq(Question)

    # this doesn't fail (thankfully)
    fail "loudly" if klass != Question

    case klass.to_s
    when Question.to_s # this doesn't match 🤔???
      tada = "🎁"
      expect(tada).to eq("🎁")
    else
      # why?????
      fail "into confusion"
    end
  end
end

另外,使用rspec是测试此意外行为的便捷方式。此问题与rspec无关。

英文:

Given the following rspec example, why is when Question not truthy?

require "rails_helper"
class Question
end

describe Question do
  it "confuses me" do
    klass = Question

    # this passes
    expect(klass).to eq(Question)

    # this doesn't fail (thankfully)
    fail "loudly" if klass != Question

    case klass
    when Question # this doesn't match 🤔???
      tada = "🎉"
      expect(tada).to eq("🎉")
    else
      # why?????
      fail "into confusion"
    end
  end
end

And no this isn't a course assignment lol. I ran into this at work and now I don't know what's true anymore.

side note, using string comparison works:

require "rails_helper"
class Question
end

describe Question do
  it "confuses me" do
    klass = Question

    # this passes
    expect(klass).to eq(Question)

    # this doesn't fail (thankfully)
    fail "loudly" if klass != Question

    case klass.to_s
    when Question.to_s # this doesn't match 🤔???
      tada = "🎉"
      expect(tada).to eq("🎉")
    else
      # why?????
      fail "into confusion"
    end
  end
end

another aside: using rspec was a convenient way to test this unexpected behavior. this issue isn't related to rspec.

答案1

得分: 4

看起来你混淆了 实例

通常,当你在 case 语句内的 when 子句中使用一个类时,你是在测试变量是否是该类的一个 实例

obj = Question.new # 注意这里的 'new'

case obj
when Question
  # obj 是 Question 的一个实例,因此会匹配。
  puts "匹配 Question"

when SomethingElse
  # SomethingElse 的实例会匹配这里。
  puts "匹配 Something else"
end

# => 输出 "匹配 Question"

在你的示例中,你不是在测试 Question 的一个实例,而是在测试 Question 本身,它是一个类,因此也是 Class 的一个实例:

klass = Question  # 没有 'new',只是 Question 本身。

case klass
when Question
  # klass 不是 Question 的一个实例,因此不会匹配...
  puts "匹配 Question"

when Class
  # klass 实际上是 Question,它是 Class 的一个实例,
  # 所以会匹配。
  puts "匹配 Class"
end

# => 输出 "匹配 Class"。

这是因为 Module.=== 的定义ClassModule 的子类):

mod === obj → true or false

Case Equality—Returns true if obj is an instance of mod or an instance of one of mod’s descendants. Of limited use for modules, but can be used in case statements to classify objects by class.

对于字符串(String),===== 的别名,因此 when 子句会基于相等性进行匹配。

英文:

It looks like you are mixing up classes and instances.

Normally when you use a class in a when clause inside a case statement, you are testing whether the variable is an instance of that class:

obj = Question.new # Note the 'new' here

case obj
when Question
  # obj is an instance of Question, so this will match.
  puts "matches Question"

when SomethingElse
  # instances of SomethingElse would match here.
  puts "matches Something else"
end

# => Outputs "matches Question"

In your example, you aren’t testing an instance of Question, but rather Question itself, which is a class and therefore an instance of Class:

klass = Question  # No 'new', just Question itself.

case klass
when Question
  # klass isn't an instance of Question, so this doesn't match...
  puts "matches Question"

when Class
  # klass actually is Question, which is an instance of Class,
  # so this does match.
  puts "matches Class"

end

# => Outputs "matches Class".

This works because of how Module.=== is defined (Class is a subclass of Module):

> mod === obj → true or false
>
> Case Equality—Returns true if obj is an instance of mod or an instance of one of mod’s descendants. Of limited use for modules, but can be used in case statements to classify objects by class.

With Strings, === is an alias of ==, so when clauses will match on equality.

答案2

得分: -1

更新:我最初发布的解决方案之所以有效,是因为进行了字符串比较,但是实际上是在实例上执行 ===,而不是在类上执行。我去掉了.to_s,将=== 转换为 self.===,现在下面的示例解决方案对我来说按照我期望的方式工作。

已更正的原始代码:
我刚刚了解到,如果我想比较两个类,它们都需要实现 self.===。下面是一个有效的示例:

require "rails_helper"
class Question
  def self.===(other)
    other == self
  end
end

describe Question do
  it "confuses me" do
    klass = Question

    # this passes
    expect(klass).to eq(Question)

    # this doesn't fail (thankfully)
    fail "loudly" if klass != Question

    case klass
    when Question
      tada = "🎉"
      expect(tada).to eq("🎉")
    else
      fail "fail? it'll never fail :)"
    end
  end
end

这是帮助我找到答案的文章链接:
https://thoughtbot.com/blog/case-equality-operator-in-ruby

英文:

Update: the original solution I posted worked because of the string comparison, but was implementing === on the instance and not on the class. I removed the .to_s and converted === into self.=== and now the example solution below works for me they way I expect.

Corrected Original:
I just learned that if I want to compare two classes, they both will need to implement self.===. Here's a working example:

require "rails_helper"
class Question
  def self.===(other)
    other == self
  end
end

describe Question do
  it "confuses me" do
    klass = Question

    # this passes
    expect(klass).to eq(Question)

    # this doesn't fail (thankfully)
    fail "loudly" if klass != Question

    case klass
    when Question
      tada = "🎉"
      expect(tada).to eq("🎉")
    else
      fail "fail? it'll never fail :) "
    end
  end
end

And here's the article that helped me find that answer:
https://thoughtbot.com/blog/case-equality-operator-in-ruby

huangapple
  • 本文由 发表于 2023年6月22日 04:56:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76527078.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定