2012-05-05 4 views
4

Во многих случаях над интернетом я видел примеры each методы для перечислимых как:Блокировка ссылок в Enumerable, разрешена?

def each(&block) 
    @items.each do |item| 
    block.call(item) 
    end 
end 

Почему люди не использует этот один:

def each(&block) 
    @items.each(&block) 
end 

Есть ли какое-либо различие?

+1

Я не уверен на 100%, так что я не буду создавать ответ, но AFAIK нет практической разницы (кроме первого, вероятно, более медленного и, безусловно, более многословного). Почему люди используют его? вероятно, потому, что это так же просто, как и в то время как второе требует некоторого понимания того, как проходить вокруг блоков/Procs. – tokland

+2

@tokland: Это не совсем правильно, первый должен будет вернуть перечислитель, если ни один блок не будет эквивалентен второму. –

+0

@Niklas, о, вы правы, теперь Ruby возвращает перечислитель, когда ни один блок не передается каждому. Еще одна причина использовать вторую. – tokland

ответ

4

На самом деле я думаю, что следующая версия еще более часто видела:

def each 
    @items.each { |i| yield i } 
end 

Это эквивалентно первый образец кода. Существует, однако, тонкое различие между этим и вашей второй версией:

class Test 
    def initialize(items) 
    @items = items 
    end 

    def each1 
    @items.each { |i| yield i } 
    end 

    def each2(&block) 
    @items.each(&block) 
    end 
end 

Наблюдайте:

irb(main):053:0> Test.new([1,2,3]).each2 
=> #<Enumerator: [1, 2, 3]:each> 
irb(main):054:0> Test.new([1,2,3]).each1 
LocalJumpError: no block given (yield) 
    from (irb):43:in `block in each1' 
    from (irb):43:in `each' 
    from (irb):43:in `each1' 
    from (irb):54 
    from /usr/bin/irb:12:in `<main>' 

версия, что делегаты блок к основной итерации фактически возвращает нумератор, если нет блока не задан, который это очень мило. Это позволяет писать такие вещи, как это:

irb(main):055:0> Test.new([1,2,3]).each2.map { |x| x + 1 } 
=> [2, 3, 4] 

Чтобы добиться того же с нашей явной версии, мы должны были бы приспособить его так:

def each1 
    return enum_for(:each1) unless block_given? 
    @items.each { |i| yield i } 
end 

Что еще более многословным. Bottomline: Делегируйте блок в базовый перечислимый, когда это возможно, чтобы спасти себя от дублирования кода и от тонких gotchas, подобных этим.

Кстати, теперь, когда вы осознаете двусложную природу своего метода each, имеет смысл называть его по-разному. Например, вы могли бы последовать примеру методов, как String#chars или IO#lines и вызове метода items:

def items(&block) 
    @items.each(&block) 
end 
+0

Спасибо, ваш третий пример кода показал мне разницу :) Похоже, я был прав :) –

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