为什么这个ruby代码没有抛出异常?

时间:2017-02-23 05:45:09

标签: ruby

我正在研究Bruce Tate的<七个七周语言,并尝试进行所有练习。有一个Tree练习,用户在其中更改初始化程序以接受哈希并从中创建树。我最初的尝试如下:

def initialize(hash={})
  if !hash.keys[0].nil?
    @node_name = hash.keys[0]
    @children = []
    if !(hash.values[0].nil? or hash.values[0] == {})
      hash.values[0].each do |k, v|
        @children.push(Tree.new({k => v})
      end
    end
  end
end

这很有效。但是,没有检查的代码也有效,即:

def initialize(hash={})
  @node_name = hash.keys[0]
  @children = []
  hash.values[0].each do |k, v|
    @children.push(Tree.new({k => v})
  end
end

为什么不需要检查?看起来我会得到类似于null引用的东西(我来自.Net背景,所以它可能在ruby中被称为其他东西)。

这是我的完整代码:

class Tree
  attr_accessor :children, :node_name

    def initialize(hash={})
      @node_name = hash.keys[0]
      @children = []
      hash.values[0].each do |k, v|
        @children.push(Tree.new({k => v})
      end
    end

  def visit_all(&block)
    visit &block
    children.each{|c| c.visit_all &block}
  end

  def visit(&block)
    block.call self
  end
end


ruby_tree = Tree.new({"grampa" => { "dad" => { "son1" => {}, "son2" => {}}, "uncle" => { "nephew1" => {}, "nephew2" => {"youngun1" => {}}}}})

puts "Visiting a node"
ruby_tree.visit {|node| puts node.node_name}
puts

puts "visiting entire tree"
ruby_tree.visit_all {|node| puts node.node_name}

2 个答案:

答案 0 :(得分:3)

如果您从此代码中删除if条件:

if !(hash.values[0].nil? or hash.values[0] == {})
  hash.values[0].each do |v|
    k = hash.key(v)
    @children.push(Tree.new({k => v})
  end
end

在这种情况下你可以得到一个例外:

hash = {}
hash.values       # => []
hash.values[0]    # => nil

如果您在不检查条件的情况下在其上运行each循环,则会失败,因为each上未定义nil

您的第二个条件hash.values[0] == {}可能会被跳过。

这只是一个简短的方法,可以防止空哈希上的each循环,因为循环对它没有影响。

hash = { foo: {} }
hash.values[0]     # => {}

现在执行{}.each对输出没有任何影响。

答案 1 :(得分:0)

如果您尝试在nil上调用方法,则只会遇到异常 - 然后您将获得NoMethodError。由于您通过了有效的哈希,因此检查并不重要。如果您在创建nil时明确地将new作为参数传递给ruby_tree,则会看到NoMethodError

此外,该行

hash.values[0].each do |k, v|

按照您的期望赢得了工作。调用hash.values返回一个数组,而数组的迭代器只产生一个项目到块(数组中下一个位置的元素)。相反,您应该使用hash.each do |k, v|