2013-05-29 2 views
4

я могу передать аргументы функции, как это:Являются ли блоки переданными в качестве аргументов?

func 1, 2, 3 

или я могу использовать скобки, как:

func(1, 2, 3) 

Позже я узнал о функциях как list.each, которые я прохожу (не уверен, если это то, что действительно происходит) блок для работы по каждому элементу:

list.each {|x| puts x} 

Я предположил, что это просто передало блок в качестве аргумента для each функция, но это, кажется, не так, потому что:

list.each({|x| puts x}) 

не работает.

Я понял это, когда показано:

5.upto(9) {|x| puts x} 

Который не имеет смысла, если блок просто аргумент.

Что здесь происходит? Любой ресурс, на который вы можете указать мне, чтобы помочь объяснить это, и, возможно, другие структурные вещи, которые не сразу очевидны?

+0

Это может быть полезной отправной точкой: [http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/](http://www.robertsosinski ,com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas /) – Chris

+0

Это специальный синтаксис, созданный специально для передачи блока функции. Для каждого вызова функции вы можете пройти один блок за пределами списка обычных аргументов. Блок по-прежнему добавляется к списку аргументов в контексте вызываемой функции. – meagar

+0

@Matt - Поскольку вы изучаете Ruby, посмотрите: http://www.codecademy.com/tracks/ruby - Действительно хороший ресурс для тех, кто хочет изучить основные основы рубинового языка – David

ответ

6

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

def greet 
    yield "Hello" 
end 

greet{ |greeting| puts "#{greeting} to you" } 

Вы также можете написать одно и то же, как это:

def greet(&block) 
    block.call("Hello") 
end 

greet{ |greeting| puts "#{greeting} to you" } 

# which is equivalent to: 
my_proc = proc{ |greeting| puts "#{greeting}, nice to see you." } 
greet(&my_proc) 

В конце концов, блоки представляют собой особый вид проками со специальным синтаксисом, что делает их более удобными. Но вы все равно можете получить доступ к процессам и передать их.

+0

это имеет большой смысл, спасибо! –

+4

+1. Я бы добавил, что есть несколько ошибок. Блоки являются технически объектами Proc, которые похожи, но не точно идентичны объектам лямбда. Их соответствующее поведение, например, Например, аргументы splat не совсем идентичны. (По общему признанию, связанные с ошибками Ruby, которые, возможно, были исправлены с тех пор, как я в последний раз выполнял какую-либо серьезную работу с использованием Ruby, но тогда были яркие тонкости, такие как это.) Также обратите внимание, что с помощью синтаксиса '& block' вы * явно * создавать объект Proc при каждом вызове - даже если ни один блок не был передан, что приводит к более медленному коду из-за избыточного создания объекта. –

4

методы могут принимать ровно один блок, вроде как специальный аргумент.

def foo 
    yield 123 
end 

foo { |x| puts x } 
#=> 123 

Обратите внимание, что этот метод принимает нулевые аргументы, но все же принимает блок. Это потому, что этот синтаксис подвески блока с помощью метода - это особый случай. Любой блок, переданный методу, может быть запущен с ключевым словом yield. И вы даже можете спросить, прошел ли блок с ключевым словом block_given?.

Если вы хотите записать его в локальной переменной, вы можете сделать это с помощью специального синтаксиса.

def foo(a, &block) 
    block.call a 
end 

foo(123) { |x| puts x } 
#=> 123 

В этом случае, вы захватывая аргумент блок в явном виде, с тем & префиксом в списке аргументов. Теперь у вас есть переменная для этого блока, которую вы можете отправить сообщение call для выполнения. Просто знайте, что это должно появиться в конце списка аргументов.

Также обратите внимание на parens. foo(123) { |x| puts x }. Список аргументов останавливается, парсеры закрываются, и прикрепленный блок появляется после. Опять же, потому что это особый случай в синтаксисе.

+0

спасибо. Можете ли вы назвать что-то вроде: 'def two_func_in (& func1, & func2)'? Как бы это выглядело? EDIT: Вы сказали, что «методы могут принимать ровно один блок». Это ответ на этот вопрос? –

+0

'def two_func_in (& func1, & func2)' недействителен ruby. Вы можете только _attach_ один блок к методу. Но есть другие способы передать в некотором коде как простой простой аргумент. Подобно объектам [Proc] (http://ruby-doc.org/core-1.9.3/Proc.html), для одного, хотя синтаксис для этого отличается. –

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