如何创建一个在Ruby中扮演假的对象

Tia*_*nyj 12 ruby

我想做这个

lol = Klass.new(values)

unless lol 
   print "false"
end

lol.other_method  # it is not nil or false, it is a Klass instance!
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,lol不是零或假,而是一个基于某种内在价值可以作为假的对象.我有这个选择

lol = Klass.new(values)

unless lol.to_bool
   print "false"
end
Run Code Online (Sandbox Code Playgroud)

但它是丑陋的恕我直言.

我在想扩展FalseClass或玩==但没有成功.有任何想法吗?

Jör*_*tag 11

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

这是Ruby不面向对象的烦人案例之一.在OO中,一个对象必须能够模拟另一个对象(实际上,取决于你询问的对象,这是OO的定义 - 记住OO来自模拟),但是不可能构建一个对象模拟false.

这是因为,在Ruby中,条件控制结构被烘焙到语言中并且不会转换为消息发送,而在其他OO语言中它们只是常规的消息发送(或者至少转换为消息发送,就像for在Ruby中转换为each).例如,在Smalltalk中,布尔值实际上是使用Lambda Calculus所知的Booleans的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
Run Code Online (Sandbox Code Playgroud)

而且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
Run Code Online (Sandbox Code Playgroud)

然后,而不是像

if 2 < 3 then foo end
if 2 < 3 then bar else baz end
Run Code Online (Sandbox Code Playgroud)

你将会拥有

(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 })
Run Code Online (Sandbox Code Playgroud)

这样,您可以轻松创建一个false仅通过实现相应方法进行模拟的对象.

在其他情况下,某些对象确实绝对必须是特定类的实例,而不只是说正确的协议,Ruby提供了一个逃生舱.例如,如果一个方法确实需要一个Array参数,那么它将首先尝试调用to_ary至少让你有机会将你的对象转换为一个Array.这同样适用于to_str,to_int,to_proc,to_float等,但没有对应的to_bool协议.


tad*_*man 6

简短回答:不要这样做

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

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

  • @TiagoPeczenyj`一个可以根据某些内部值充当假的对象` -&gt; 只需添加一个 `false?` 方法来回答该内部值,然后添加 `unless lol.false?`。如果我没有弄错的话,它并不难看,但是很好的 Ruby 鸭子打字。 (2认同)