ruby中if / unless语句的隐含值

时间:2013-11-17 23:08:43

标签: ruby

我是Ruby的新手,并试图理解为什么这样可行(它似乎确实如此,至少根据"the Master"):

def array_of_fixnums?(array)
    true unless array.find { |item| item.class != Fixnum }
end

我担心的是当数组包含非fixnum值时,“false-ness”来自何处。我是否正确地假设else false声明中没有隐含的“unless”?在这种情况下,我认为它必须来自nil返回的Enumerable#find值。这是对的吗?

如果是这样,那似乎有点不稳定。可能最好明确地返回false,这样吗?

array.find { |item| item.class != Fixnum } ? false : true

还有另一种更好的方法吗?感谢您帮助我解决这个问题,以及任何“最佳实践”建议。

3 个答案:

答案 0 :(得分:3)

您的方法返回nil并不是因为find返回nil,而是因为如果您的内联条件未通过,则该方法没有明确的返回值。如果它不是内联的,那就更清楚了,考虑一下:

def array_of_fixnums?(array)
  unless array.find { |item| item.class != Fixnum }
    return true
  end
  # otherwise don't explicitly return (implicit nil)
end

虽然依赖于nil的虚假通常会工作,但问题在于它不遵循最不惊讶的原则。 <{1}}方法应该返回true或false。

但是你的方法有更糟糕的问题。它使用令人困惑的逻辑(双重否定),而本身依赖于?的虚假和 nil的真实性来发挥作用。考虑如果您的方法通过nil会发生什么。糟糕。

更好的方法是:

[false]

理由是,这种方法完全符合它的说法。

明确地返回布尔值,虽然不一定是错误的,但是多余的并且通常被认为是不好的做法。而是考虑的例子,在ruby中,是这个数组中每个值的一个Fixnum?表达式的结果就是该方法的结果;没有理由对其进行评估,然后返回array.all? {|n| n.is_a? Fixnum }

答案 1 :(得分:1)

从find方法doc:

Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.

因此,您只需要:

def array_of_fixnums?(array)
    array.find { |item| item.class != Fixnum }
end

如果找到任何内容,将返回该内容,否则将返回nil,并且该方法将评估为false。

但是,由于项目返回可能是falsenil(如果列表中的任何项目为false或nil),我建议您使用.any?方法。

def array_of_fixnums?(array)
    array.any? { |item| item.class != Fixnum }
end

答案 2 :(得分:0)

让我们看看为什么true unless condition能够正常工作。

我相信x unless conditionx if !condition的美学改进,但两者是等价的(而且,在我看来,这是一种改进)。为了摆脱双重否定,我只考虑x if condition

Ruby的一个基本原则是每个语句都必须返回一个值(即最后一个表达式)。因此,x if condition必须评估为if condition then x else y end。但是什么是y?如果y评估为truefalse,则当x if condition是布尔表达式时,x将毫无意义。要了解原因,请考虑:

w = (x == 5 if z == 6)

如果w属实,我们可以得出结论x = 5z = 6,但如果w为假,我们就不知道z = 6x != 5,或z != 6以及x = 5x != 5。考虑到除y以外的nil的任何值都被评估为truefalsey的唯一合理值为nil。这样,if w == nil可用。当然,当x可能是nil时,这仍然是一个问题(在其他情况下)。

我欢迎澄清和阐述。也许还有一种不太复杂的方式来论证。