No Ruby, os métodos podem receber um bloco de código para executar segmentos arbitrários de código.
Quando um método espera um bloco, ele o chama chamando a yieldfunção
Isso é muito útil, por exemplo, para percorrer uma lista ou fornecer um algoritmo personalizado.
Veja o seguinte exemplo:
Vou definir uma Personclasse inicializada com um nome e fornecer um do_with_namemétodo que, quando chamado, passaria o nameatributo para o bloco recebido.
classPersondef initialize( name )@name= nameenddef do_with_name yield(@name)endend
Isso nos permitiria chamar esse método e passar um bloco de código arbitrário.
Por exemplo, para imprimir o nome, faríamos:
person =Person.new("Oscar")#invoking the method passing a block
person.do_with_name do|name|
puts "Hey, his name is #{name}"end
Imprimiria:
Hey, his name is Oscar
Observe que o bloco recebe, como parâmetro, uma variável chamada name(NB, você pode chamar essa variável como quiser, mas faz sentido chamá-la name). Quando o código chama, yieldele preenche esse parâmetro com o valor de @name.
yield(@name)
Poderíamos fornecer outro bloco para executar uma ação diferente. Por exemplo, inverta o nome:
#variable to hold the name reversed
reversed_name =""#invoke the method passing a different block
person.do_with_name do|name|
reversed_name = name.reverseend
puts reversed_name=>"racsO"
Usamos exatamente o mesmo método ( do_with_name) - é apenas um bloco diferente.
Este exemplo é trivial. Usos mais interessantes são filtrar todos os elementos em uma matriz:
days =["monday","tuesday","wednesday","thursday","friday"]# select those which start with 't'
days.select do| item |
item.match /^t/end=>["tuesday","thursday"]
Ou também podemos fornecer um algoritmo de classificação personalizado, por exemplo, com base no tamanho da string:
Desculpe, o nome é uma variável de instância inicializada com "Oscar" (não é muito clara na resposta) #
OscarRyz 21/11/12
E o código assim? person.do_with_name {|string| yield string, something_else }
f.ardelian
7
Portanto, em termos de Javascripty, é uma maneira padronizada de passar um retorno de chamada para um determinado método e chamá-lo. Obrigada pelo esclarecimento!
precisa saber é o seguinte
De uma maneira mais geral - um bloco é um açúcar de sintaxe "aprimorado" em ruby para o padrão Strategy. porque o uso típico é fornecer um código para fazer algo no contexto de outra operação. Mas melhorias rubi abrir um caminho para essas coisas legais como escrever DSLs usando bloco para passar contexto ao redor
Roman Bulgakov
25
No Ruby, os métodos podem verificar se foram chamados de tal maneira que um bloco foi fornecido além dos argumentos normais. Normalmente, isso é feito usando o block_given?método, mas você também pode se referir ao bloco como um Proc explícito, prefixando um e comercial ( &) antes do nome do argumento final.
Se um método for chamado com um bloco, ele poderá yieldcontrolar o bloco (chamar o bloco) com alguns argumentos, se necessário. Considere este método de exemplo que demonstra:
def foo(x)
puts "OK: called as foo(#{x.inspect})"yield("A gift from foo!")if block_given?end
foo(10)# OK: called as foo(10)
foo(123){|y| puts "BLOCK: #{y} How nice =)"}# OK: called as foo(123)# BLOCK: A gift from foo! How nice =)
Ou, usando a sintaxe especial do argumento de bloco:
def bar(x,&block)
puts "OK: called as bar(#{x.inspect})"
block.call("A gift from bar!")if block
end
bar(10)# OK: called as bar(10)
bar(123){|y| puts "BLOCK: #{y} How nice =)"}# OK: called as bar(123)# BLOCK: A gift from bar! How nice =)
É bom conhecer maneiras diferentes de acionar um bloco.
LPing
22
É bem possível que alguém forneça uma resposta verdadeiramente detalhada aqui, mas eu sempre achei este post de Robert Sosinski uma ótima explicação das sutilezas entre blocos, procs e lambdas.
Devo acrescentar que acredito que o post ao qual estou vinculando é específico do ruby 1.8. Algumas coisas mudaram no ruby 1.9, como variáveis de bloco sendo locais no bloco. No 1.8, você obteria algo como o seguinte:
>> a ="Hello"=>"Hello">>1.times{|a| a ="Goodbye"}=>1>> a
=>"Goodbye"
Considerando que 1.9 daria a você:
>> a ="Hello"=>"Hello">>1.times{|a| a ="Goodbye"}=>1>> a
=>"Hello"
Eu não tenho 1.9 nesta máquina, portanto, o acima pode ter um erro.
@klenwell obrigado pelo aviso, atualizei o link novamente.
theIV 07/07
13
Eu queria acrescentar por que você faria as coisas dessa maneira com as já excelentes respostas.
Não faço ideia de que idioma você é, mas, supondo que seja um idioma estático, esse tipo de coisa parecerá familiar. É assim que você lê um arquivo em java
publicclassFileInput{publicstaticvoid main(String[] args){File file =newFile("C:\\MyFile.txt");FileInputStream fis =null;BufferedInputStream bis =null;DataInputStream dis =null;try{
fis =newFileInputStream(file);// Here BufferedInputStream is added for fast reading.
bis =newBufferedInputStream(fis);
dis =newDataInputStream(bis);// dis.available() returns 0 if the file does not have more lines.while(dis.available()!=0){// this statement reads the line from the file and print it to// the console.System.out.println(dis.readLine());}// dispose all the resources after using them.
fis.close();
bis.close();
dis.close();}catch(FileNotFoundException e){
e.printStackTrace();}catch(IOException e){
e.printStackTrace();}}}
Ignorando todo o encadeamento do fluxo, a ideia é essa
rir dos caras de java que ainda estão digitando ;-)
Aqui, em vez de lidar com as etapas um e dois, você basicamente delega isso para outra classe. Como você pode ver, isso reduz drasticamente a quantidade de código que você precisa escrever, o que facilita a leitura e reduz as chances de coisas como vazamentos de memória ou bloqueios de arquivos que não são limpos.
Agora, não é como se você não pudesse fazer algo semelhante em java; de fato, as pessoas fazem isso há décadas. É chamado de padrão de estratégia . A diferença é que, sem blocos, para algo simples como o exemplo de arquivo, a estratégia se torna um exagero devido à quantidade de classes e métodos que você precisa escrever. Com blocos, é uma maneira tão simples e elegante de fazê-lo, que não faz sentido NÃO estruturar seu código dessa maneira.
Esta não é a única maneira pela qual os blocos são usados, mas os outros (como o padrão Builder, que você pode ver no formulário_para API nos trilhos) são semelhantes o suficiente para que seja óbvio o que está acontecendo quando você entender isso. Quando você vê blocos, geralmente é seguro assumir que a chamada do método é o que você deseja fazer, e o bloco está descrevendo como você deseja fazê-lo.
Vamos simplificar um pouco: File.readlines("readfile.rb").each_with_index do |line, index| puts "#{index + 1}: #{line}" ende rir ainda mais dos caras do Java.
Michael Hampton
1
@ MichaelHampton, ria depois de ler um arquivo com alguns gigabytes.
akostadinov 17/02/2015
@akostadinov Não ... isso me faz querer chorar!
Michael Hampton
3
@MichaelHampton Ou, melhor ainda: IO.foreach('readfile.rb').each_with_index { |line, index| puts "#{index}: #{line}" }(sem problemas de memória)
Fund Monica's Lawsuit
12
Eu achei este artigo muito útil. Em particular, o seguinte exemplo:
#!/usr/bin/rubydef test
yield5
puts "You are in the method test"yield100end
test {|i| puts "You are in the block #{i}"}
test do|i|
puts "You are in the block #{i}"end
que deve fornecer a seguinte saída:
You are in the block 5You are in the method test
You are in the block 100You are in the block 5You are in the method test
You are in the block 100
Então, essencialmente, toda vez que uma chamada é feita para yieldruby, o código é executado no dobloco ou no interior {}. Se um parâmetro for fornecido yield, ele será fornecido como um parâmetro para o dobloco.
Para mim, foi a primeira vez que entendi realmente o que os doblocos estavam fazendo. É basicamente uma maneira de a função conceder acesso a estruturas de dados internas, seja para iteração ou para configuração da função.
Então, quando nos trilhos, você escreve o seguinte:
Isso executará a respond_tofunção que gera o dobloco com o formatparâmetro (interno) . Você então chama a .htmlfunção nessa variável interna que, por sua vez, gera o bloco de código para executar o rendercomando. Observe que .htmlsomente produzirá se for o formato de arquivo solicitado. (tecnicidade: essas funções na verdade block.callnão são usadas yieldcomo você pode ver na fonte, mas a funcionalidade é essencialmente a mesma; consulte esta pergunta para uma discussão.) Isso fornece um meio para a função executar alguma inicialização, depois receber informações do código de chamada e Em seguida, continue o processamento, se necessário.
Em outras palavras, é semelhante a uma função que usa uma função anônima como argumento e a chama em javascript.
No Ruby, um bloco é basicamente um pedaço de código que pode ser passado e executado por qualquer método. Os blocos são sempre usados com métodos, que geralmente alimentam dados para eles (como argumentos).
Os blocos são amplamente utilizados em gemas Ruby (incluindo Rails) e em código Ruby bem escrito. Eles não são objetos, portanto, não podem ser atribuídos a variáveis.
Sintaxe básica
Um bloco é um pedaço de código delimitado por {} ou do..end. Por convenção, a sintaxe entre chaves deve ser usada para blocos de linha única e a sintaxe do..end deve ser usada para blocos de várias linhas.
{# This is a single line block }do# This is a multi-line blockend
Qualquer método pode receber um bloco como argumento implícito. Um bloco é executado pela declaração de rendimento dentro de um método. A sintaxe básica é:
def meditate
print "Today we will practice zazen"yield# This indicates the method is expecting a blockend# We are passing a block as an argument to the meditate method
meditate { print " for 40 minutes."}Output:Today we will practice zazen for40 minutes.
Quando a declaração de rendimento é alcançada, o método meditate gera controle para o bloco, o código dentro do bloco é executado e o controle é retornado ao método, que retoma a execução imediatamente após a declaração de rendimento.
Quando um método contém uma declaração de rendimento, espera receber um bloco no momento da chamada. Se um bloco não for fornecido, uma exceção será lançada assim que a declaração de rendimento for alcançada. Podemos tornar o bloco opcional e evitar que uma exceção seja gerada:
def meditate
puts "Today we will practice zazen."yieldif block_given?end meditate
Output:Today we will practice zazen.
Não é possível passar vários blocos para um método. Cada método pode receber apenas um bloco.
OK mas porquê ? Existem várias razões, como a Loggerque não deve ser executada, se o usuário não precisar. Você deve explicar o seu embora ...
Ulysse BN
4
Rendimentos, para simplificar, permitem que o método que você cria receba e chame blocos. A palavra-chave yield é especificamente o local onde as 'coisas' no bloco serão executadas.
Há dois pontos que quero destacar sobre o rendimento aqui. Primeiro, embora muitas respostas aqui falem sobre diferentes maneiras de passar um bloco para um método que usa yield, vamos também falar sobre o fluxo de controle. Isso é especialmente relevante, pois você pode gerar vários tempos em um bloco. Vamos dar uma olhada em um exemplo:
classFruit
attr_accessor :kinds
def initialize
@kinds=%w(orange apple pear banana)enddef each
puts 'inside each'3.times{yield(@kinds.tap {|kinds| puts "selecting from #{kinds}"}).sample }endend
f =Fruit.new
f.each do|kind|
puts 'inside block'end=> inside each
=> selecting from ["orange","apple","pear","banana"]=> inside block
=> selecting from ["orange","apple","pear","banana"]=> inside block
=> selecting from ["orange","apple","pear","banana"]=> inside block
Quando o método each é invocado, ele executa linha por linha. Agora, quando chegarmos ao bloco 3. vezes, esse bloco será chamado 3 vezes. Cada vez que invoca rendimento. Esse rendimento está vinculado ao bloco associado ao método que chamou o método each. É importante observar que cada vez que o rendimento é invocado, ele retorna o controle de volta ao bloco de cada método no código do cliente. Quando o bloco termina de executar, ele volta ao bloco 3.x vezes. E isso acontece 3 vezes. Portanto, esse bloco no código do cliente é chamado em 3 ocasiões separadas, pois o rendimento é explicitamente chamado 3 vezes em separado.
Meu segundo ponto é sobre enum_for e yield. enum_for instancia a classe Enumerator e esse objeto Enumerator também responde ao rendimento.
Portanto, observe que toda vez que invocamos tipos com o iterador externo, ele invocará yield apenas uma vez. A próxima vez que chamarmos, ele invocará o próximo rendimento e assim por diante.
Há um boato interessante em relação a enum_for. A documentação online declara o seguinte:
enum_for(method =:each,*args)→ enum
Creates a new Enumerator which will enumerate by calling method on obj, passing args if any.
str ="xyz"
enum = str.enum_for(:each_byte)
enum.each {|b| puts b }# => 120# => 121# => 122
Se você não especificar um símbolo como argumento para enum_for, o ruby conectará o enumerador a cada método do destinatário. Algumas classes não possuem um método para cada, como a classe String.
str ="I like fruit"
enum = str.to_enum
enum.next=>NoMethodError: undefined method `each' for "I like fruit":String
Portanto, no caso de alguns objetos invocados com enum_for, você deve ser explícito quanto ao método de enumeração.
O rendimento pode ser usado como bloco sem nome para retornar um valor no método Considere o seguinte código:
DefUp(anarg)yield(anarg)end
Você pode criar um método "Up" ao qual é atribuído um argumento. Agora você pode atribuir esse argumento ao rendimento que chamará e executará um bloco associado. Você pode atribuir o bloco após a lista de parâmetros.
Up("Here is a string"){|x| x.reverse!; puts(x)}
Quando o método Up chama yield, com um argumento, ele é passado para a variável de bloco para processar a solicitação.
Respostas:
Sim, é um pouco intrigante a princípio.
No Ruby, os métodos podem receber um bloco de código para executar segmentos arbitrários de código.
Quando um método espera um bloco, ele o chama chamando a
yield
funçãoIsso é muito útil, por exemplo, para percorrer uma lista ou fornecer um algoritmo personalizado.
Veja o seguinte exemplo:
Vou definir uma
Person
classe inicializada com um nome e fornecer umdo_with_name
método que, quando chamado, passaria oname
atributo para o bloco recebido.Isso nos permitiria chamar esse método e passar um bloco de código arbitrário.
Por exemplo, para imprimir o nome, faríamos:
Imprimiria:
Observe que o bloco recebe, como parâmetro, uma variável chamada
name
(NB, você pode chamar essa variável como quiser, mas faz sentido chamá-laname
). Quando o código chama,yield
ele preenche esse parâmetro com o valor de@name
.Poderíamos fornecer outro bloco para executar uma ação diferente. Por exemplo, inverta o nome:
Usamos exatamente o mesmo método (
do_with_name
) - é apenas um bloco diferente.Este exemplo é trivial. Usos mais interessantes são filtrar todos os elementos em uma matriz:
Ou também podemos fornecer um algoritmo de classificação personalizado, por exemplo, com base no tamanho da string:
Espero que isso ajude você a entender melhor.
BTW, se o bloco for opcional, você deve chamá-lo como:
Se não for opcional, basta invocá-lo.
EDITAR
@hmak criou um repl.it para estes exemplos: https://repl.it/@makstaks/blocksandyieldsrubyexample
fonte
racsO
sethe_name = ""
"Oscar"
(não é muito clara na resposta) #person.do_with_name {|string| yield string, something_else }
No Ruby, os métodos podem verificar se foram chamados de tal maneira que um bloco foi fornecido além dos argumentos normais. Normalmente, isso é feito usando o
block_given?
método, mas você também pode se referir ao bloco como um Proc explícito, prefixando um e comercial (&
) antes do nome do argumento final.Se um método for chamado com um bloco, ele poderá
yield
controlar o bloco (chamar o bloco) com alguns argumentos, se necessário. Considere este método de exemplo que demonstra:Ou, usando a sintaxe especial do argumento de bloco:
fonte
É bem possível que alguém forneça uma resposta verdadeiramente detalhada aqui, mas eu sempre achei este post de Robert Sosinski uma ótima explicação das sutilezas entre blocos, procs e lambdas.
Devo acrescentar que acredito que o post ao qual estou vinculando é específico do ruby 1.8. Algumas coisas mudaram no ruby 1.9, como variáveis de bloco sendo locais no bloco. No 1.8, você obteria algo como o seguinte:
Considerando que 1.9 daria a você:
Eu não tenho 1.9 nesta máquina, portanto, o acima pode ter um erro.
fonte
Eu queria acrescentar por que você faria as coisas dessa maneira com as já excelentes respostas.
Não faço ideia de que idioma você é, mas, supondo que seja um idioma estático, esse tipo de coisa parecerá familiar. É assim que você lê um arquivo em java
Ignorando todo o encadeamento do fluxo, a ideia é essa
É assim que você faz em rubi
Descontroladamente diferente. Quebrando este aqui
Aqui, em vez de lidar com as etapas um e dois, você basicamente delega isso para outra classe. Como você pode ver, isso reduz drasticamente a quantidade de código que você precisa escrever, o que facilita a leitura e reduz as chances de coisas como vazamentos de memória ou bloqueios de arquivos que não são limpos.
Agora, não é como se você não pudesse fazer algo semelhante em java; de fato, as pessoas fazem isso há décadas. É chamado de padrão de estratégia . A diferença é que, sem blocos, para algo simples como o exemplo de arquivo, a estratégia se torna um exagero devido à quantidade de classes e métodos que você precisa escrever. Com blocos, é uma maneira tão simples e elegante de fazê-lo, que não faz sentido NÃO estruturar seu código dessa maneira.
Esta não é a única maneira pela qual os blocos são usados, mas os outros (como o padrão Builder, que você pode ver no formulário_para API nos trilhos) são semelhantes o suficiente para que seja óbvio o que está acontecendo quando você entender isso. Quando você vê blocos, geralmente é seguro assumir que a chamada do método é o que você deseja fazer, e o bloco está descrevendo como você deseja fazê-lo.
fonte
File.readlines("readfile.rb").each_with_index do |line, index| puts "#{index + 1}: #{line}" end
e rir ainda mais dos caras do Java.IO.foreach('readfile.rb').each_with_index { |line, index| puts "#{index}: #{line}" }
(sem problemas de memória)Eu achei este artigo muito útil. Em particular, o seguinte exemplo:
que deve fornecer a seguinte saída:
Então, essencialmente, toda vez que uma chamada é feita para
yield
ruby, o código é executado nodo
bloco ou no interior{}
. Se um parâmetro for fornecidoyield
, ele será fornecido como um parâmetro para odo
bloco.Para mim, foi a primeira vez que entendi realmente o que os
do
blocos estavam fazendo. É basicamente uma maneira de a função conceder acesso a estruturas de dados internas, seja para iteração ou para configuração da função.Então, quando nos trilhos, você escreve o seguinte:
Isso executará a
respond_to
função que gera odo
bloco com oformat
parâmetro (interno) . Você então chama a.html
função nessa variável interna que, por sua vez, gera o bloco de código para executar orender
comando. Observe que.html
somente produzirá se for o formato de arquivo solicitado. (tecnicidade: essas funções na verdadeblock.call
não são usadasyield
como você pode ver na fonte, mas a funcionalidade é essencialmente a mesma; consulte esta pergunta para uma discussão.) Isso fornece um meio para a função executar alguma inicialização, depois receber informações do código de chamada e Em seguida, continue o processamento, se necessário.Em outras palavras, é semelhante a uma função que usa uma função anônima como argumento e a chama em javascript.
fonte
No Ruby, um bloco é basicamente um pedaço de código que pode ser passado e executado por qualquer método. Os blocos são sempre usados com métodos, que geralmente alimentam dados para eles (como argumentos).
Os blocos são amplamente utilizados em gemas Ruby (incluindo Rails) e em código Ruby bem escrito. Eles não são objetos, portanto, não podem ser atribuídos a variáveis.
Sintaxe básica
Um bloco é um pedaço de código delimitado por {} ou do..end. Por convenção, a sintaxe entre chaves deve ser usada para blocos de linha única e a sintaxe do..end deve ser usada para blocos de várias linhas.
Qualquer método pode receber um bloco como argumento implícito. Um bloco é executado pela declaração de rendimento dentro de um método. A sintaxe básica é:
Quando a declaração de rendimento é alcançada, o método meditate gera controle para o bloco, o código dentro do bloco é executado e o controle é retornado ao método, que retoma a execução imediatamente após a declaração de rendimento.
Quando um método contém uma declaração de rendimento, espera receber um bloco no momento da chamada. Se um bloco não for fornecido, uma exceção será lançada assim que a declaração de rendimento for alcançada. Podemos tornar o bloco opcional e evitar que uma exceção seja gerada:
Não é possível passar vários blocos para um método. Cada método pode receber apenas um bloco.
Veja mais em: http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html
fonte
Às vezes eu uso "yield" assim:
fonte
Logger
que não deve ser executada, se o usuário não precisar. Você deve explicar o seu embora ...Rendimentos, para simplificar, permitem que o método que você cria receba e chame blocos. A palavra-chave yield é especificamente o local onde as 'coisas' no bloco serão executadas.
fonte
Há dois pontos que quero destacar sobre o rendimento aqui. Primeiro, embora muitas respostas aqui falem sobre diferentes maneiras de passar um bloco para um método que usa yield, vamos também falar sobre o fluxo de controle. Isso é especialmente relevante, pois você pode gerar vários tempos em um bloco. Vamos dar uma olhada em um exemplo:
Quando o método each é invocado, ele executa linha por linha. Agora, quando chegarmos ao bloco 3. vezes, esse bloco será chamado 3 vezes. Cada vez que invoca rendimento. Esse rendimento está vinculado ao bloco associado ao método que chamou o método each. É importante observar que cada vez que o rendimento é invocado, ele retorna o controle de volta ao bloco de cada método no código do cliente. Quando o bloco termina de executar, ele volta ao bloco 3.x vezes. E isso acontece 3 vezes. Portanto, esse bloco no código do cliente é chamado em 3 ocasiões separadas, pois o rendimento é explicitamente chamado 3 vezes em separado.
Meu segundo ponto é sobre enum_for e yield. enum_for instancia a classe Enumerator e esse objeto Enumerator também responde ao rendimento.
Portanto, observe que toda vez que invocamos tipos com o iterador externo, ele invocará yield apenas uma vez. A próxima vez que chamarmos, ele invocará o próximo rendimento e assim por diante.
Há um boato interessante em relação a enum_for. A documentação online declara o seguinte:
Se você não especificar um símbolo como argumento para enum_for, o ruby conectará o enumerador a cada método do destinatário. Algumas classes não possuem um método para cada, como a classe String.
Portanto, no caso de alguns objetos invocados com enum_for, você deve ser explícito quanto ao método de enumeração.
fonte
O rendimento pode ser usado como bloco sem nome para retornar um valor no método Considere o seguinte código:
Você pode criar um método "Up" ao qual é atribuído um argumento. Agora você pode atribuir esse argumento ao rendimento que chamará e executará um bloco associado. Você pode atribuir o bloco após a lista de parâmetros.
Quando o método Up chama yield, com um argumento, ele é passado para a variável de bloco para processar a solicitação.
fonte