微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Ruby 中相等运算符的顺序重要吗?

如何解决Ruby 中相等运算符的顺序重要吗?

我在我的 Ruby 程序中使用了 bcrypt 库。我注意到相等运算符的顺序似乎很重要。根据'=='左边或右边的变量,我得到不同的结果。 这是一个示例程序:

require 'bcrypt'
my_pw = "pw1"
puts "This is my unhashed password: #{my_pw}"
hashed_pw = BCrypt::Password.create(my_pw)
puts "This is my hashed password: #{hashed_pw}"

20.times{print"-"}
puts

puts "my_pw == hashed_pw equals:"
if (my_pw == hashed_pw)
  puts "TRUE"
else
  puts "FALSE"
end

puts "hashed_pw == my_pw equals:"
if (hashed_pw == my_pw)
  puts "TRUE"
else
  puts "FALSE"
end

问候 尚德

解决方法

是的,有区别。

my_pw == hashed_pw== 字符串上调用 my_pw 方法并将 hashed_pw 作为参数传递。这意味着您正在使用 String#== 方法。来自docs of String#==

string == object → true or false

如果true具有相同的长度和内容,则返回object;如selffalse 否则

hashed_pw == my_pw== 的实例上调用 BCrypt::Password 方法并将 my_pw 作为参数传递。来自 BCrypt::Password#== 的文档:

#==(secret) ⇒ Object

将潜在的秘密与散列进行比较。如果秘密是原始秘密,则返回 true,否则返回 false

,

这与平等没有任何关系。这只是面向对象编程的基础。

在 OOP 中,所有计算都是由对象向其他对象发送消息来完成的。 OOP 的一个基本属性是接收者对象并且只有接收者对象决定如何响应这条消息。这就是在 OOP 中实现封装、数据隐藏和抽象的方式。

因此,如果您将消息 m 发送到对象 a 并传递 b 作为参数,那么 a 将决定如何解释此消息。如果您将消息 m 发送到对象 b 并传递 a 作为参数,那么由 b 决定如何解释此消息。没有内置机制可以保证 ab 解释此消息相同。 只有如果两个对象决定相互协调,响应实际上是相同的。

如果你仔细想想,如果2 - 33 - 2的结果相同,那就太奇怪了。

这正是这里发生的事情:在第一个示例中,您将消息 == 发送到 my_pw,将 hashed_pw 作为参数传递。 my_pwString class 的一个实例,因此 == 消息将被分派到 String#== 方法。此方法知道如何将接收器对象与另一个 String 进行比较。但是,它不知道如何将接收者与 BCrypt::Password 进行比较,这就是 hashed_pw 的类。

如果你仔细想想,这是有道理的:BCrypt::Password 是一个来自 Ruby 之外的第三方类,一个内置的 Ruby 类如何知道一些没有的东西在实现 String 类时甚至存在

另一方面,在您的第二个示例中,您将消息 == 发送到 hashed_pw,将 my_pw 作为参数传递。此消息被分派到 BCrypt::Password#== 方法,该方法确实知道如何将接收者与 String 进行比较:

方法:BCrypt::Password#==

定义在:lib/bcrypt/password.rb

#==(secret)Object
也称为:is_password?

将潜在的秘密与散列进行比较。如果秘密是原始秘密,则返回 true,否则返回 false

实际上,这种特殊情况下的问题比乍看起来更微妙。

我在上面写过 String#== 不知道如何处理 BCrypt::Password 作为参数,因为它只知道如何比较 String。嗯,实际上 BCrypt::Password 继承自 String,这意味着 BCrypt::Password IS-A String,所以 String#== 应该 知道如何处理它!

但是想想String#==的作用:

string == objecttruefalse

如果true具有相同的长度和内容,则返回object;如selffalse 否则 […]

考虑一下:“如果 true 具有相同的长度和内容,则返回 object”。对于散列,这实际上永远不会是真的。 self 将类似于 'P@$$w0rd!'object 将类似于 '$2y$12$bxWYpd83lWyIr.dF62eO7.gp4ldf2hMxDofXPwdDZsnc2bCE7hN9q',因此很明显,它们的长度和内容都不相同。并且object 不可能是相同的内容,因为加密安全密码哈希的全部意义在于您无法逆转它。因此,即使 object 想以某种方式将自己显示为原始密码,它也无法做到。

唯一可行的方法是,如果 StringBCrypt::Password 可以以某种方式“一起工作”以找出相等性。

现在,如果我们仔细查看 String#== 的文档,实际上有一种方法可以使这项工作:

如果 object 不是 String 的实例而是响应 to_str,则使用 object.== 比较两个字符串。

因此,如果 BCrypt::Password 的作者做出了不同的设计决定,那么它会起作用:

  1. 不要让BCrypt::Password继承String
  2. 实施BCrypt::Password#to_str。这实际上允许 BCrypt::PasswordString 实际上互换使用,因为任何接受 String 的方法应该接受响应 to_str 的对象.

现在,根据 String#== 的文档,如果您编写 my_pw == hashed_pw,会发生以下情况:

  1. String#== 注意到 hashed_pw 不是 String
  2. String#== 注意到 hashed_pw 确实回应了 to_str
  3. 因此,它将发送消息 object == self(在我们的例子中相当于 hashed_pw == my_pw),这意味着我们现在处于您问题的第二种情况,效果很好。
  4. 立>

这是一个如何工作的例子:

class Pwd
  def initialize(s)
    @s = s.downcase.freeze
  end

  def to_str
    p __callee__
    @s.dup.freeze
  end

  def ==(other)
    p __callee__,other
    @s == other.downcase
  end

end

p = Pwd.new('HELLO')
s = 'hello'

p == s
# :==
# "hello"
#=> true

s == p
# :==
# "hello"
#=> true

如您所见,我们得到了预期的结果,并且 Pwd#== 两次都被调用。此外,to_str 永远不会被调用,它只会被 String#== 检查。

所以,具有讽刺意味的是,问题不在于String#==不知道如何处理BCrypt::Password对象,而实际上问题在于它确实 知道如何将它们作为通用String处理。如果他们不是 String 而只是回应 to_str,那么 String#== 实际上会知道向他们寻求帮助。

Ruby 中的

Numeric 对象具有完整的 coercion protocol 以确保即使对于第三方数字库也支持不同“类数字”操作数类型之间的算术运算。

,

例如,如果两个操作数都是 String 类型,则表达式将是等效的。在您的情况下,一个操作数是 String,另一个是 BCrypt::Password。因此,my_pw == hashed_pw 调用 String 类中定义的相等方法,而 hashed_pw == my_pw 调用 BCrypt::Password 中定义的方法。

我从来没有使用过 BCrypt::Password,但我希望你为前者获得 false,为后者获得 true

,

在 Ruby 中,您可以覆盖给定类或实例的相等方法:

final groupedList = groupBy(dayTaskList,(DayTasks dayTasks) => DateTime(dayTasks._date.year)).entries.toList();

一般来说,在不使其对称的情况下覆盖相等被认为是不好的做法,但您有时会在野外看到它。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。