Подтвердить что ты не робот

Лучшее объяснение блоков Ruby?

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

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

4b9b3361

Ответ 1

Я предлагаю свое объяснение из этого ответа, слегка измененный:

"Блоки" в Ruby - это не то же самое, что и общие термины программирования "блок кода" или "блок кода".

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

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.

Ответ 2

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

  • Конструкция как объект Proc (или с помощью ключевого слова lambda):

    add1 = Proc.new {|x| x+1} # Returns its argument plus one.
    add1.call(1) # => 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.
    

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

Ответ 3

Из Почему (острый) путеводитель по рубину:

Любой код, окруженный фигурными фигурными скобками, блок.

2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } - пример.

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

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 | являются аргументами. После аргументов мы имеют немного кода. Выражение x + y добавляет два аргумента вместе. я как думать о символах трубы как представляющий туннель. Они дают появление лотка, что переменные сползают вниз. (An x идет вниз орла, а y аккуратно пересекает ее ноги.) Этот желоб действует как проход между блоками и окружающий их мир. Переменные проходил через этот желоб (или туннель) в блок.

Ответ 4

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

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

Ruby: { |x| x + 1 }

С#: x => x + 1

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

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

Оба С# и Ruby предлагают альтернативные способы записи приведенного выше примера.

Ruby:

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

С#:

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

В Ruby и С# допускаются несколько операторов, в Ruby для этого требуется второй синтаксис.

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

Ответ 5

В книге " Programming Ruby есть большое объяснение блоков и используя их.

В 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

Также существует оператор "splat" *, который работает в списке параметров:

do |a,*b| 
  some_stuff
end

Назначит первое из нескольких значений значение "a", а все остальные будут записаны в "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

Ответ 6

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

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

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

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

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

Блоки (в ruby ​​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 и {} блоками? По соглашению {} блоки находятся в одной строке, а блоки..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>

Ответ 7

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

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