14

我想做这个

lol = Klass.new(values)

unless lol 
   print "false"
end

lol.other_method  # it is not nil or false, it is a Klass instance!

但是在这种情况下,lol 不是 nil 或 false,而是一个可以根据某些内部值充当 false 的对象。我有这个选择

lol = Klass.new(values)

unless lol.to_bool
   print "false"
end

但它是丑陋的恕我直言。

我正在考虑扩展 FalseClass 或玩 == 但没有成功。有任何想法吗?

4

2 回答 2

16

不幸的是,这是不可能的。

这是 Ruby 不是面向对象的那些烦人的情况之一。在 OO 中,一个对象必须可以模拟另一个对象(实际上,这取决于您问谁,这就是 OO 的定义——记住 OO 来自模拟),但不可能构建一个对象模拟false.

这是因为,在 Ruby 中,条件控制结构被嵌入到语言中并且不会转换为消息发送,而在其他 OO 语言中它们只是常规消息发送(或至少转换为消息发送,就像for在 Ruby 中转换为each)。例如,在 Smalltalk 中,布尔值实际上是使用您从 Lambda 演算中知道的布尔值的 Church 编码来实现的,并转换为 Ruby,它们看起来有点像这样:

class FalseClass
  def if(&block)
    # do nothing
  end

  def if_then_else(then_lambda, else_lambda)
    else_lambda.()
  end

  def not
    true
  end

  def and(&block)
    self
  end

  def or(&block)
    block.()
  end
end

而且TrueClass只是镜像:

class TrueClass
  def if(&block)
    block.()
  end

  def if_then_else(then_lambda, else_lambda)
    then_lambda.()
  end

  def not
    false
  end

  def and(&block)
    block.()
  end

  def or(&block)
    self
  end
end

然后,而不是像

if 2 < 3 then foo end
if 2 < 3 then bar else baz end

你将会拥有

(2 < 3).if { foo }
(2 < 3).if_then_else(-> { bar }, -> { baz })

# using the new keyword arguments in Ruby 2.0, it almost reads like Smalltalk:
class FalseClass
  def if(then: -> {}, else: -> {})
    else.()
  end
end

class TrueClass
  def if(then: -> {}, else: -> {})
    then.()
  end
end

(2 < 3).if(then: -> { bar }, else: { baz })

这样,您可以轻松地创建一个false简单地通过实现相应方法来模拟的对象。

在其他情况下,某些对象确实必须是特定类的实例,而不仅仅是使用正确的协议,Ruby 提供了一个逃生口。例如,如果一个方法确实需要 aArray作为参数,那么它会首先尝试调用to_aryto 至少给你一个机会将你的对象转换为Array. to_str, to_int,to_proc等也是如此to_float。但没有等效的to_bool协议。

于 2013-01-22T00:01:34.553 回答
6

简短的回答:不要这样做

长答案是,在 Ruby 中只有两件事评估为假:falsenil. 从字面上看,其他一切都评估为真。

许多应用程序依赖于这种行为,如果你设法改变它,可能会出现故障。这就像将 1 定义为偶数一样。它会引起问题。

于 2013-01-21T19:18:37.520 回答