添加数字时,如何将nil值视为零?

时间:2016-02-06 16:25:38

标签: ruby monkeypatching

我有一个看起来像这样的方法:

def calculate_the_thing(hsh)
  hsh[:x] + hsh[:y] + hsh[:z]
end

采用这样的方式:

{:x => 5, :y => nil, :z => 2, :a => 5}

我想修补一些类,以便当+方法获得零值时,它会将其视为零。似乎合理。我怎么能这样做?

2 个答案:

答案 0 :(得分:7)

正如@jforberg所指出的那样,你可以使用#to_i方法,该方法将返回0为零。

def calculate_the_thing(hsh)
  hsh[:x].to_i + hsh[:y].to_i + hsh[:z].to_i
end

或者,您也可以使用自动默认值...

定义哈希
hsh = Hash.new{0}

但是如果你有一个方法明确地将nil作为一个将覆盖默认值的哈希值。

答案 1 :(得分:5)

你需要修补Nilclass以强制nil为整数。这是代码:

class NilClass
  def coerce(n)
    return [0, n] if n.is_a? Numeric
    super
  end
end

1 + nil
#=> 1

看看这个帖子 - In Ruby, how does coerce() actually work? - 了解coerce的概念。

但是,上述代码存在问题:

nil + 1
#=> undefined method `+' for nil:NilClass (NoMethodError)

要解决此问题,您必须在+

上定义NilClass方法
class NilClass
  def +(param)
    param  + self
  end
end

nil + 1
#=> 1

如果我们尝试冒险并尝试:

nil * 10
#=> undefined method `*' for nil:NilClass (NoMethodError)

通过冒险,让我们通过实现我们自己的undefined method处理程序来处理所有这些method_missing

class NilClass
  def method_missing m, *args, &block
    args.first.send(m, self, &block) if args.size == 1
  end
end

p nil * 1
#=> 0

接下来,让我们试试:

nil + "hello"
# '+': can't convert NilClass to String (NilClass#to_str gives NilClass) (TypeError)

让我们解决这个问题

class NilClass
  def to_str
    ""
  end
end

nil + "hello"
#=> "hello"

接下来,让我们试试这个:

nil + [1,2,3]
#=> '+': can't convert NilClass to Array (NilClass#to_ary gives NilClass) (TypeError)

让我们解决它:

class NilClass
  def to_ary
    []
  end
end

nil + [1,2,3]
#=> [1, 2, 3]

我们现在有NilClass的这个版本:

class NilClass
  def coerce(n)
    return [0, n] if n.is_a? Numeric
    super
  end

  def method_missing m, *args, &block
    args.first.send(m, self, &block) if args.size == 1
  end

  def to_str
    ""
  end

  def to_ary
    []
  end
end

警告: :上面的代码显示您可以完成的工作。但是,这应该仅用于实验和学习目的。在所有操作中使nil表现得像其他操作数一样真的是不可行的,并且你最终会将NilClass修改为def lookup_permille(name): # TODO: implement a permille lookup table return 0 def lookup_known_product(name): # TODO: implement a known product lookup table return 0 class ProductEntry: def __init__(self, source, name, price, volume, permille = lookup_permille(name), known_product_id = lookup_known_product(name), category = 0): self.source = source self.name = name self.price = price self.volume = volume self.permille = permille self.price_per_permille = self.permille / self.price; self.category = category self.known_product_id = known_product_id 。 因此,最好避免使用这种猴子修补程序,以避免对将要维护代码的未来Rubyist带来可怕的惊喜。