2011-02-06 5 views
33

Какое лучшее объяснение для блоков Ruby, которые вы можете использовать?Лучшее объяснение блоков Ruby?

Как код использования, так и код, который может принимать блок?

+1

Вы ищете введение в концепцию блоков или исчерпывающую ссылку на них? – Phrogz

+16

Или вы просто троллитесь за репутацией, задавая вопросы, на которые вам не нужны ответы, не намерены принимать и не намерены даже участвовать в обсуждении? Посмотрим, если вы ответите. – Phrogz

+0

Это полезная тема: http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ – Lucio

ответ

6

Книга "Programming Ruby" имеет отличное explanation of blocks and using them.

В 1.9+, список параметров передается в блок стал более сложным, что позволяет локальные переменные должны быть определены:

do |a,b;c,d| 
    some_stuff 
end 

;c,d объявить два новых локальных переменных внутри блока, которые не получают значения из вызываемой подпрограммой yield. Ruby 1.9+ гарантирует, что, если бы переменные существовали вне блока, они не будут топтаны одноименными переменными внутри блока. Это новое поведение; 1.8 будет топать на них.

def blah 
    yield 1,2,3,4 
end 

c = 'foo' 
d = 'bar' 

blah { |a, *b; c,d| 
    c = 'hello' 
    d = 'world' 
    puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}", "d: #{d}" 
} 

puts c, d 
# >> a: 1 
# >> b: 2,3,4 
# >> c: hello 
# >> d: world 
# >> foo 
# >> bar 

Там же «знак» оператор *, который работает в списке параметров:

do |a,*b| 
    some_stuff 
end 

присвоит первый из нескольких значений «а», а все остальные будут захвачены в «b», который будет обрабатываться как массив. * может быть переменной a:

do |*a,b| 
    some_stuff 
end 

бы захватить все прошло в переменных, за исключением последнего, который будет принят в b. И, так же, как два предыдущих:

do |a,*b,c| 
    some_stuff 
end 

бы присвоить первое значение a, последнее значение c и все/любые промежуточные значения в b.

Я думаю, что это довольно мощный и гладкий.

Например:

def blah 
    yield 1,2,3,4 
end 

blah { |a, *b| puts "a: #{a}", "b: #{b.join(',')}" } 
# >> a: 1 
# >> b: 2,3,4 

blah { |*a, b| puts "a: #{a.join(',')}", "b: #{b}" } 
# >> a: 1,2,3 
# >> b: 4 

blah { |a, *b, c| puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}" } 
# >> a: 1 
# >> b: 2,3 
# >> c: 4 
19

От Why's (poignant) guide to ruby:

Любой код в фигурные скобки является блока.

2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } является примером.

С помощью блоков вы можете сгруппировать набор команд , чтобы они могли передавать вашу программу по адресу . В фигурных скобках видны краб-клещи, которые выхватили код и держат его вместе. Когда вы видите эти два клеща, помните , что код внутри был нажат в один блок.Это как один из этих маленьких Hello Kitty коробков они продают в торговом центре, который фаршированный крошечных карандашей и микроскопической бумагой, все ютятся в блестящем прозрачный корпус, который может быть скрытой в вашей ладони для тайных стационарных операций. За исключением того, что блоки не требуют так много щуриться. Кудрявые фигурные скобки также можно продавать для слов do и end, что приятно, если ваш блок длиннее одной строки.

loop do 
    print "Much better."  
    print "Ah. More space!" 
    print "My back was killin' me in those crab pincers." 
end 

Блок аргументы являются набор переменных, окруженных трубы символов, разделенных запятыми.

|x|, |x,y|, and |up, down, all_around| are examples. 

Блок аргументы используются в начале блока.

{ |x,y| x + y } 

В приведенном выше примере, | х, у | являются аргументами. После аргументов у нас есть немного кода. Выражение x + y объединяет два аргумента. I нравится думать о символах трубы как представляющий туннель. Они дают внешний вид желоба, который сбрасывает переменные . (А х идет вниз орлиный орел, а y аккуратно пересекает ее ноги.) Этот желоб действует как проход между блоками и окружающий мир . Переменные прошли через этот желоб (или туннель) в блок.

+16

«Любой код, окруженный фигурными фигурными скобками, является блоком», если только это ** хэш **. – Meltemi

+1

Вы не объясните, как вернутся эти примеры. Я не понимаю. –

+0

Прошу быть репетитором! Спасибо, что объяснили это таким простым и понятным образом. – Benjamints

3

Блоки - это способ группировки кода в Ruby. Существует два способа записи блоков. Один использует оператор do..end, а другой окружает код в фигурных скобках: {}. Блоки считаются объектами на языке программирования Ruby, и по умолчанию все функции принимают неявный блок-аргумент.

Вот два примера блоков, которые делают то же самое:

 
2.times { puts 'hi' } 
2.times do 
    puts 'hi' 
end 

Блоки могут получить списки разделенных запятыми аргументов внутри вертикальных столбиков ||. Например:

 
[1,2].map{ |n| n+2 } # [3, 4] 

блоков (в рубин 1.9.2) можно явно иметь локальные переменные:

 
x = 'hello' 
2.times do |;x| 
    x = 'world' 
    puts x 
end 

=> world 
=> world 

Локальные переменные могут быть объединены с параметрами:

 
[1,2].map{ |n;x| n+2 } 

Все функции могут получить аргумент блока по умолчанию:

 
def twice 
    yield 
    yield 
end 

twice { puts 'hello' } 
=> hello 
=> hello 

В чем разница между do..end и {} блоками? По соглашению {} блоки находятся на одной строке и делают ..концевые блоки охватывают несколько строк, поскольку каждый из них легче читать таким образом. Основное различие связано с старшинства, хотя:

 
array = [1,2] 

puts array.map{ |n| n*10 } # puts (array.map{ |n| n*10 }) 
=> 10 
=> 20 

puts array.map do |n| n*10 end # (puts array.map) do |n| n*10 end 
=> <Enumerator:0x00000100862670> 
2

Блоки легкие литералы для анонимных процедур первого класса с некоторыми раздражающими ограничениями. Они работают так же, как в Ruby, так как они работают в значительной степени любой другой язык программирования, по модулю выше упомянутых ограничений, которые:

  • блоки могут появляться только в списках аргументов
  • более одного блока может появиться в списке аргументов (и он должен быть последний аргумент)
+0

Хороший ответ, но отношение к объектам Proc кажется существенным, нет? – maerics

+0

@maerics Существенный для исчерпывающего ресурса на блоках? Да. Существенное для объяснения блоков (которое я интерпретирую как введение в них для новичков)? Определенно нет, ИМО. – Phrogz

+0

Спасибо. Ваш единственный ответ, который помог мне понять, почему '{puts" hello "}' не работает. Не разрешено вообще? Это странно. –

27

Я предлагаю мое собственное объяснение от this answer, слегка модифицирован:

«Блоки» в Ruby, не то же самые, как общие термины программирования " кодовый блок "o r "блок кода".

Притворитесь на мгновение, что следующий (недействительный) Рубиновый код на самом деле работал:

def add10(n) 
    puts "#{n} + 10 = #{n+10}" 
end 

def do_something_with_digits(method) 
    1.upto(9) do |i| 
    method(i) 
    end 
end 

do_something_with_digits(add10) 
#=> "1 + 10 = 11" 
#=> "2 + 10 = 12" 
... 
#=> "9 + 10 = 19" 

Хотя этот код является недействительным, его намерение, минуя некоторый код к способу и имеющему, что метод выполнения кода, является возможно в Ruby различными способами. Одним из таких способов является «Блоки».

Блок в Ruby очень, очень похож на метод: он может принимать некоторые аргументы и запускать код для них. Всякий раз, когда вы видите foo{ |x,y,z| ... } или foo do |x,y,z| ... end, это блоки, которые принимают три параметра и запускают на них .... (Вы даже можете увидеть, что метод upto выше передается в блок.)

Поскольку блоки являются особой частью синтаксиса Ruby, каждый метод имеет право передать блок. Использует ли метод блок до метода. Например:

def say_hi(name) 
    puts "Hi, #{name}!" 
end 

say_hi("Mom") do 
    puts "YOU SUCK!" 
end 
#=> Hi, Mom! 

выше метод передается в блок, который готов выдать оскорбление, но так как метод не вызывает блок, только хорошо печатается сообщение. Вот как мы называем блок из метода:

def say_hi(name) 
    puts "Hi, #{name}!" 
    if block_given? 
    yield(name) 
    end 
end 

say_hi("Mridang") do |str| 
    puts "Your name has #{str.length} letters." 
end 
#=> Hi, Mridang! 
#=> Your name has 7 letters. 

Мы используем block_given?, чтобы увидеть, был ли принят блок вместе или нет. В этом случае мы передали аргумент обратно в блок; это зависит от вашего метода, чтобы решить, что перейти к блоку. Например:

def say_hi(name) 
    puts "Hi, #{name}!" 
    yield(name, name.reverse) if block_given? 
end 

say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" } 
#=> Hi, Mridang! 
#=> Is your name Mridang or gnadirM? 

Это просто соглашение (и хороший один и один вы хотите поддержать) для некоторых классов, чтобы передать экземпляр только что созданный блок.

Это не исчерпывающий ответ, поскольку он не охватывает блокирование блоков в качестве аргументов, как они обрабатывают arity, un-splatting в параметрах блока и т. Д., Но намереваются служить в качестве введения Blocks-Are-Lambdas.

26

Рубиновые блоки - это способ создания Proc objects, которые представляют код, который может использоваться другим кодом.Объектами Proc являются команды между фигурными фигурными скобками {} (или do...end фразы для многострочных блоков, которые имеют более низкий приоритет, чем фигурные скобки), которые могут необязательно принимать аргументы и возвращать значения (например, {|x,y| x+y}). Procs являются first-class objects и могут быть построены явно или достигается неявно, как метод псевдо-аргументов:

  1. Строительство в качестве Proc объекта (или используя lambda ключевое слово):

    add1 = Proc.new {|x| x+1} # Returns its argument plus one. 
    add1.call(1) # => 2 
    
  2. Зачет как способ псевдо аргумент, либо явно используя специальный оператор синтаксиса синтаксиса последнего аргумента &, либо неявно используя пару block_given?/yield:

    def twice_do(&proc) # "proc" is the block given to a call of this method. 
        2.times { proc.call() } if proc 
    end 
    twice_do { puts "OK" } # Prints "OK" twice on separate lines. 
    
    def thrice_do() # if a block is given it can be called with "yield". 
        3.times { yield } if block_given? 
    end 
    thrice_do { puts "OK" } # Prints "OK" thrice on separate lines. 
    

Вторая форма обычно используется для Visitor patterns; данные могут быть переданы аргументам специального блока в качестве аргументов методам call или yield.

+4

Скобки имеют высокий приоритет; 'do' имеет низкий приоритет. Если вызов метода имеет параметры, которые не заключены в круглые скобки, форма скобки блока будет привязываться к последнему параметру, а не к общему вызову. Форма 'do' будет привязана к вызову. – Green

+0

Почему downvote? – maerics

+2

английский язык, пожалуйста! ...... «Блоки Ruby - это синтаксические литералы для объектов Proc ....» - если люди не знают, что такое блок, я предполагаю, что они не будут знать, что такое «синтаксические литералы для объектов Proc» означает либо , попробуйте объяснить, как если бы читателям было 5 лет. – BKSpurgeon

6

Для кого подходит к этому вопросу с C# фоне (или других LANGS на самом деле), это может помочь:

Рубиновые блоки, как лямбда-выражений и анонимных методов в C#. Это то, что C# вызывает делегатов (и Ruby вызывает Procs), то есть они суть функции, которые могут передаваться как значения. В Ruby и C# они также могут вести себя как замыкания.

Ruby: { |x| x + 1 }

C#: x => x + 1

Ruby: { |name| puts "Hello there #{name}" }

C#: name => { Console.WriteLine("Hello there {0}", name); }

Оба C# и Руби предлагают альтернативные способы, чтобы написать пример выше.

Рубин:

do |name| 
    puts "Hello there #{name}" 
end 

C#

delegate(string name) 
{ 
    Console.WriteLine("Hello there {0}", name); 
} 

В обоих Рубин и C#, несколько операторов разрешено, В Ruby, второй синтаксис выше для этого требуется.

Эти концепции доступны на многих других языках, на которые повлияли идеи функционального программирования.

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