Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Ruby: Общие вопросы > Сущность yield statement


Автор: Andi 13.9.2006, 12:21
В Руби есть интересная языковая конструкция под названием
yield statement.Обьясните на пальцах ее физический смысл.
Хорошо бы ссылки (или коды программ) с конкретным применением.
Кто что знает,отзовитесь!

Автор: simanyay 13.9.2006, 17:48
Чисто-конкретное применение в Rails

Есть основной шаблон (layout):

Код

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html>
  <head>
    <title>Чисто-конкретно</title>
  </head>
  <body>
    <div id="left_column">
      <%= yield :left %>
    </div>
    <div id="right_column">
      <%= yield :right %>
    </div>
  </body>
</html>


При помощи такого кода мы в разных вьюшках можем пихать в эти места разный код. Пример:
Код

<% content_for :left do %>
  <p>левота</p>
<% end %>

<% content_for :right do %>
   <p>правота</p>
<% end %>


Таким образом на выходе будет:
Код

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html>
  <head>
    <title>Чисто-конкретно</title>
  </head>
  <body>
    <div id="left_column">
      <p>левота</p>
    </div>
    <div id="right_column">
      <p>правота</p>
    </div>
  </body>
</html>


Будет время, напишу примеры из голенького ruby.

Автор: Pete 13.9.2006, 20:23
yield позволяет вызывать переданный методу блок кода:

Код

def f
  puts 'begin'
  yield
  puts 'end'
end

f { puts 'center' }

результат:
Код

begin
center
end


Также можно вызывать блок с параметрами, передаваемыми при вызове yield:

Код

def f
  yield( 10, '3' )
end

f {|a, b| puts b*a }   # => 3333333333


«Свои методы с блоками» на http://ru.wikibooks.org/wiki/Ruby#.D0.A1.D0.B2.D0.BE.D0.B8_.D0.BC.D0.B5.D1.82.D0.BE.D0.B4.D1.8B_.D1.81_.D0.B1.D0.BB.D0.BE.D0.BA.D0.B0.D0.BC.D0.B8

Автор: max_lapshin 13.9.2006, 21:28
Тут идея в чем. Блоки — часть функционального программирования.

Мы берем какую-то функциональность в виде объекта, но запускать ее будем только тогда, когда она нам будет нужна.
Аналог — взять адрес функции в C, после чего запустить ее по необходимости. Тоже самое, только гораздо примитивнее.

Соответственно, yield — полный аналог процедуры запуска функции не по ее прямому имени, а через переменную, в которую ее записали.

Разница в том, что при запуске метода может быть передан блок. В описании метода он никак не фигурирует (впрочем, его можно задать явно), соответственно, yield — вызов того самого единственного блока, который был передан неявно в метод.

Автор: Andi 14.9.2006, 08:21
Выходит что yield это анонимная рекурсия,правильно я понял?

Автор: Pete 14.9.2006, 09:18
Не совсем. yield вызывает только ассоциированный с функцией блок кода. Функция не вызывает сама себя (в общем случае), она лишь обрабатывает некоторый код, переданный ей при вызове.

Автор: skalex 14.9.2006, 15:45
Ребята, объясните попроще, а? smile

Автор: Pete 14.9.2006, 20:20
skalex, куда уж проще?  smile 
Как правильно выразился max_lapshin, конструкцию yield можно понимать как тело некоторой функции, которая вызывается использованием ключевого слова yield. Примеры даны вполне очевидные.

Добавлено @ 20:24 
В моем последнем http://forum.vingrad.ru/index.php?showtopic=111720&view=findpost&p=853589:
Код

def f
  yield( 10, '3' )
end
f {|a, b| puts b*a }   # => 3333333333

фактически написано вот что:
Код

def f
  a, b = 10, '3'  # a = 10; b = '3'
  puts b*a  # b раз напечатать переменную a
end

Автор: Void 14.9.2006, 21:26
Цитата(max_lapshin @  13.9.2006,  23:28 Найти цитируемый пост)
Соответственно, yield — полный аналог процедуры запуска функции не по ее прямому имени, а через переменную, в которую ее записали.

Не совсем. yield и блоки — это скорее ближе к сопроцедурам. Есть одно весьма существенное отличие от функций высшего порядка: из блоков Ruby возможен нелокальный возврат. Вызывая функцию, мы можем быть уверены, что она вернёт нам управление. Даже если будет выброшено исключение, мы будем иметь возможность перехватить его. Блок же может передать управление в контекст, в котором он был создан.
Например:
Код
def hof(f) # функция высшего порядка
    puts f # вызываем переданную в качестве параметра функцию
end

def foo
    puts 'We are in foo'
    return 'foo return value'
end

def block_yield
    puts yield
    # Если переданный блок сделает return, 
    # мы сюда никогда не попадём!
    puts 'Normal exit'
end

def caller
    block_yield {
        puts 'In the block'
        'return some value to the block caller'
    }
    block_yield {
        puts 'In the block'
        return 'non-local return'
    }
end

puts 'Higher order function:'
hof(foo)
puts 'Blocks:'
puts caller

Автор: max_lapshin 15.9.2006, 00:44
Код

> block_yield { "lala"; return false;}
LocalJumpError: unexpected return
        from (irb):6
        from (irb):2:in `block_yield'
        from (irb):6
        from :0

Автор: Void 15.9.2006, 07:59
Цитата(max_lapshin @  15.9.2006,  02:44 Найти цитируемый пост)
LocalJumpError: unexpected return

Естественно. Куда ему return'аться с toplevel? А если бы блок с return был создан в контексте функции, то и возвращался бы из этой функции. Приведённый мной выше код отрабатывает, как предполагается, на 1.8.5.

Автор: Andi 15.9.2006, 08:40
Примеры очень даже ничего.
А еще говорят что Руби легок в понимании и освоении!!!
!!!Это неправда!!!
Да на уровне путсов и стрингов с аррэями все понятно.
Но когда начинается высший пилотаж-тут уж извините!
Надо больше примеров и притом с разьяснениями!
Если кто то еще чего нибудь подкинет будет весьма интересно!

Автор: max_lapshin 15.9.2006, 09:27
2Andi: говорят, что С++ легок в понимании. Это говорят люди, которые не видели шаблонов =)

Концепция блоков вообще достаточно непростая, поэтому нельзя здесь хотеть примитивных решений.
Примеров дохрена: открываем сырцы рельс и читаем. Если неясно, почему оно и как, открываем сырцы руби и смотрим. Я делаю так.

Автор: Pete 15.9.2006, 23:00
Цитата(Andi @  15.9.2006,  09:40 Найти цитируемый пост)
А еще говорят что Руби легок в понимании и освоении!!!

Конечно, легок. На начальной стадии обучения. Только вряд ли на этой стадии нужен yield. Наличие сложных конструкций никак не уменьшает количество простых.

Автор: Void 15.9.2006, 23:03
Цитата(max_lapshin @  15.9.2006,  11:27 Найти цитируемый пост)
говорят, что С++ легок в понимании.

 smile Кто? Мне такие безумцы ещё не встречались smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)