2014-02-19 1 views
4

я нашел следующий код here для устранения повторяющихся записей в массиве:Выхода в наборе для устранения в массиве

require 'set' 

class Array 
    def uniq_by 
    seen = Set.new 
    select{ |x| seen.add?(yield(x)) } 
    end 
end 

И мы можем использовать код выше, следующим образом:

@messages = Messages.all.uniq_by { |h| h.body } 

I хотел бы знать, как и что происходит при вызове метода. Может ли кто-нибудь объяснить внутренности кода выше? В методе uniq_by мы не сделали ничего, чтобы обрабатывать аргумент блока. Как передается аргумент uniq_by?

+3

'Array.uniq' занимает блок с [Ruby 1.9.3] (http://ruby-doc.org/core-1.9.3/Array.html#method-i-uniq), поэтому нет необходимости для этого кода (больше). – steenslag

ответ

1

В Ruby, когда вы кладете yield ключевое слово внутри любого метода (скажем #bar), вы явно сообщая #bar, что вы будете использовать блок с помощью метода #bar. Таким образом, yield знает, что внутри блока метода будет преобразован объект Proc, и yield должны называть это Proc объектом.

Пример:

def bar 
    yield 
end 

p bar { "hello" } # "hello" 
p bar # bar': no block given (yield) (LocalJumpError) 

In the uniq_by method, we did not do anything to handle block argument. How is the passed argument handled by uniq_by method?

Вы действительно делали, что вы положили yield. После того, как вы поместите этот yield, теперь метод очень умный, чтобы знать, что это так. В строке Messages.all.uniq_by { |h| h.body } вы передаете блок { |h| h.body }, а внутри определения метода uniq_by этот блок был преобразован в объект Proc, а yield - Proc#call.

Доказательство:

def bar 
    p block_given? # true 
    yield 
end 

bar { "hello" } # "hello" 

лучше для понимания:

class Array 
    def uniq_by 
    seen = Set.new 
    select{ |x| seen.add?(yield(x)) } 
    end 
end 

такой же, как

class Array 
    def uniq_by 
    seen = Set.new 
    # Below you are telling uniq_by, you will be using a block with it 
    # by using `yield`. 
    select{ |x| var = yield(x); seen.add?(var) } 
    end 
end 

Прочитайте документ о yield

Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. If no code block has been supplied, calling yield raises an exception. yield can take an argument; any values thus yielded are bound to the block's parameters. The value of a call to yield is the value of the executed code block.

+0

Но последний аргумент этого метода выбирается, поэтому, если вы вызываете метод uniq_by без аргументов, что произойдет? – pramod

+0

@pramod ** Не аргумент **, скажем ** без блока ** k..Вы получите сообщение об ошибке ..try сейчас. –

+0

Да, точно, но это ошибка из-за выбора или нашего вызова выхода? – pramod

2

Метод uniq_by принимает аргумент блока. Это позволяет указать, по каким критериям вы хотите идентифицировать два элемента как «уникальные».

Оператор yield будет оценивать значение данного блока для элемента и возвращать значение атрибута атрибута body. Итак, если вы вызываете unique_by, как указано выше, вы заявляете, что атрибут body элементов должен быть уникальным для уникального элемента.

Чтобы ответить на более конкретный вопрос, у вас есть: yield будет вызывать переданный блок {|h| h.body} как метод, заменяющий h для текущего x и поэтому возвращение x.body

+5

почему downvotes? – MoMolog

+0

Почему вы делаете это без изменений и не даете простой комментарий? это совершенно правильный ответ. – MoMolog

+0

Да .. Я согласен +1 –

3

Давайте разбить его:

seen = Set.new 

Создать пустой набор

select{ |x| seen.add?(yield(x)) } 

Array#select будет сохранять элементы, когда блок дает true.

seen.add?(yield(x)) вернет true, если результат может быть добавлен в набор или false, если он не может.

Действительно, yield(x) будет вызывать блок, переданный методу uniq_by, и передать x в качестве аргумента.

В нашем случае, так как наш блок { |h| h.body }, он будет таким же, как вызов seen.add?(x.body)

Поскольку набор уникален, вызывая add?, когда уже существует элемент будет возвращать ложь.

Таким образом, он попытается вызвать .body на каждый элемент массива и добавить его в набор, сохранив элементы, в которых возможно добавление.

+0

Пожалуйста, объясните, как работает yield (x). – pramod

+0

@pramod просто оценивает пройденный блок. –

+0

@MarekLipka, где метод uniq_by обрабатывает аргумент блока? – pramod

1

Array#select возвращает новый массив, содержащий все элементы массива, для которых данный блок возвращает истинное значение.

Аргумент блока select использует Set#add?, чтобы определить, существует ли этот элемент. add? возвращает nil, если в наборе уже есть тот же элемент, в противном случае он возвращает сам набор и добавляет элемент в набор.

Блок снова передает аргумент (элемент массива) в другой блок (блок передан в uniq_by) с использованием yield; Возврат значение yield является возвращаемым значением блока ({|h| h.body })

select .. утверждения в основном аналогичен следующим утверждение:

select{ |x| seen.add?(x.body) } 

Но используя yield, код избежать жесткого кодирования .body, и отдает решение блоку.

+0

где метод uniq_by обрабатывает аргумент блока? – pramod

+0

@pramod. Аргумент 'yield' (' x') передается блоку ('{| h | ...}'). – falsetru

+0

@pramod в 'yield (x)'. Вы оцениваете блок, передавая ему 'x' в качестве аргумента. –

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