2017-02-23 5 views
1

Я работаю через Bruce Tate's Семь языков за семь недель, и пытается выполнить все упражнения. Существует упражнение Tree, в котором пользователь должен изменить только инициализатор, чтобы принять хэш и создать дерево из него. Моя первоначальная попытка была следующая:Почему этот код ruby ​​не генерирует исключение?

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 

Почему проверка не требуется? Похоже, я получаю что-то вроде нулевой ссылки (я берусь из фона .Net, поэтому его можно назвать чем-то еще в рубине).

Вот мой полный код:

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} 
+0

Небольшая вещь: вы можете написать '@children = hash.values ​​[0] .map {| k, v | Tree.new (k => v)} '. Ruby позволяет писать хэш без брекетов, когда это аргумент. –

+0

Просто для полноты: в Ruby нет нулевых ссылок. Когда-либо. Любая ссылка всегда будет на действительный объект. –

+0

Ах. Итак, в худшем случае, это был бы NoSuchMerhod на объекте Nil? – tjcertified

ответ

2

Если удалить условие 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 не будет иметь никакого эффекта на выходе.

0

Вы получите только исключение, если попытаетесь вызвать метод на nil - тогда вы получите NoMethodError. Поскольку вы прошли в действительных хэшах, проверки не имеют значения. Если вы явно прошли nil в качестве аргумента new при создании ruby_tree, вы увидите NoMethodError

Кроме того, линия

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

не будет работать, как вы ожидаете. Вызов hash.values возвращает массив, и итератор для массивов возвращает только один элемент в блок (элемент в следующей позиции в массиве). Вместо этого вы должны использовать hash.each do |k, v|

Смежные вопросы