Mas esse não é um método aninhado. Repito: Ruby não tem métodos aninhados.
O que isto é, é uma definição de método dinâmico. Quando você executa meth1, o corpo de meth1será executado. O corpo apenas define um método chamado meth2, e é por isso que, depois de executar meth1uma vez, você pode chamar meth2.
Mas onde está meth2definido? Bem, obviamente não é definido como um método aninhado, uma vez que não existem métodos aninhados em Ruby. É definido como um método de instância de Test1:
Test1.new.meth2
# Yay
Além disso, obviamente será redefinido sempre que você executar meth1:
Test1.new.meth1
# Yay
Test1.new.meth1
# test1.rb:3: warning: method redefined; discarding old meth2# test1.rb:3: warning: previous definition of meth2 was here# Yay
Resumindo: não, Ruby não suporta métodos aninhados.
Observe também que em Ruby, corpos de método não podem ser fechamentos, apenas corpos de bloco podem. Isso praticamente elimina o principal caso de uso para métodos aninhados, já que mesmo se Ruby suportasse métodos aninhados, você não poderia usar as variáveis do método externo no método aninhado.
ATUALIZAÇÃO CONTINUADA: em um estágio posterior , então, esta sintaxe pode ser reutilizada para adicionar métodos aninhados ao Ruby, que se comportariam da maneira que descrevi: eles teriam como escopo seu método de contenção, ou seja, invisível e inacessível fora de seu método de contenção corpo. E, possivelmente, eles teriam acesso ao escopo léxico do método de contenção. No entanto, se você ler a discussão que vinculei acima, poderá observar que matz é fortemente contra métodos aninhados (mas ainda para remover definições de métodos aninhados).
Você também pode mostrar, no entanto, como criar um lambda de fechamento em um método para DRYness ou como executar a recursão.
Phrogz
119
Estou sentindo que Ruby pode não ter métodos aninhados.
Mark Thomas
16
@Mark Thomas: Eu esqueci de mencionar que Ruby não tem métodos aninhados? :-) Sério: no momento em que eu escrevi essa resposta, já havia três respostas, cada uma das quais afirmavam que Ruby faz tem métodos aninhados. Algumas dessas respostas tiveram até votos positivos, apesar de estarem totalmente erradas. Um até foi aceito pelo OP, novamente, apesar de estar errado. O trecho de código que a resposta usa para provar que Ruby suporta métodos aninhados, na verdade prova o oposto, mas aparentemente nem os upvoters nem o OP realmente se preocuparam em verificar. Então, eu dei uma resposta certa para cada resposta errada. :-)
Jörg W Mittag
10
Quando você percebe que tudo isso são apenas instruções para o Kernel que modificam as tabelas e que os métodos, classes e módulos são apenas entradas nas tabelas e não são reais, você se torna como Neo quando ele vê a aparência da Matrix. Então você poderia realmente se tornar filosófico e dizer que além dos métodos aninhados, não existem nem mesmo métodos. Não há nem mesmo agentes. Eles são programas da matriz. Mesmo aquele bife suculento que você está comendo é apenas uma entrada em uma mesa.
mydoghasworms
3
Não há métodos, seu código é apenas uma simulação em The Matrix
bbozo
13
Na verdade, é possível. Você pode usar procs / lambda para isso.
deftest(value)
inner = ->() {
value * value
}
inner.call()
end
Você não está errado, mas sua resposta foi formulada como uma solução para obter métodos aninhados. Quando na realidade você está apenas usando procs que não são métodos. É uma boa resposta além de alegar resolver "métodos aninhados"
Brandon Buck
5
Não, não, Ruby tem métodos aninhados. Verifique isto:
defouter_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x"# prints "x", "y"
Isso é útil quando você está fazendo coisas como escrever DSLs que requerem compartilhamento de escopo entre métodos. Mas, do contrário, é muito melhor você fazer qualquer outra coisa, porque, como as outras respostas disseram, inneré redefinido sempre que outerfor invocado. Se você deseja esse comportamento, e às vezes deseja, esta é uma boa maneira de obtê-lo.
O jeito do Ruby é fingir com hacks confusos que farão alguns usuários se perguntarem "Como diabos isso funciona?", Enquanto os menos curiosos simplesmente memorizarão a sintaxe necessária para usar a coisa. Se você já usou Rake ou Rails, já viu esse tipo de coisa.
Aqui está um tal hack:
defmlet(name,func)
my_class = (Class.new dodefinitialize(name,func)@name=name
@func=func
enddefmethod_missing(methname, *args)
puts "method_missing called on #{methname}"if methname == @name
puts "Calling function #{@func}"@func.call(*args)
else
raise NoMethodError.new "Undefined method `#{methname}' in mlet"endendend)
yield my_class.new(name,func)
end
O que isso faz é definir um método de nível superior que cria uma classe e a passa para um bloco. A classe costuma method_missingfingir que possui um método com o nome que você escolheu. Ele "implementa" o método chamando o lambda que você deve fornecer. Ao nomear o objeto com um nome de uma letra, você pode minimizar a quantidade de digitação extra que ele requer (que é a mesma coisa que o Rails faz nele schema.rb). mleté nomeado após a forma Common Lisp flet, exceto onde fsignifica "função", msignifica "método".
É possível fazer uma engenhoca semelhante que permite que várias funções internas sejam definidas sem aninhamento adicional, mas que requer um hack ainda mais feio do tipo que você pode encontrar na implementação de Rake ou Rspec. Descobrir como o Rspec let!funciona levaria você a um longo caminho para ser capaz de criar uma abominação tão horrível.
Respostas:
ATUALIZAÇÃO: uma vez que esta resposta parece ter despertado algum interesse ultimamente, eu gostaria de apontar que há uma discussão sobre o rastreador de problemas Ruby para remover o recurso discutido aqui, ou seja, proibir ter definições de método dentro de um corpo de método .
Não, Ruby não tem métodos aninhados.
Você pode fazer algo assim:
class Test1 def meth1 def meth2 puts "Yay" end meth2 end end Test1.new.meth1
Mas esse não é um método aninhado. Repito: Ruby não tem métodos aninhados.
O que isto é, é uma definição de método dinâmico. Quando você executa
meth1
, o corpo demeth1
será executado. O corpo apenas define um método chamadometh2
, e é por isso que, depois de executarmeth1
uma vez, você pode chamarmeth2
.Mas onde está
meth2
definido? Bem, obviamente não é definido como um método aninhado, uma vez que não existem métodos aninhados em Ruby. É definido como um método de instância deTest1
:Test1.new.meth2 # Yay
Além disso, obviamente será redefinido sempre que você executar
meth1
:Test1.new.meth1 # Yay Test1.new.meth1 # test1.rb:3: warning: method redefined; discarding old meth2 # test1.rb:3: warning: previous definition of meth2 was here # Yay
Resumindo: não, Ruby não suporta métodos aninhados.
Observe também que em Ruby, corpos de método não podem ser fechamentos, apenas corpos de bloco podem. Isso praticamente elimina o principal caso de uso para métodos aninhados, já que mesmo se Ruby suportasse métodos aninhados, você não poderia usar as variáveis do método externo no método aninhado.
ATUALIZAÇÃO CONTINUADA: em um estágio posterior , então, esta sintaxe pode ser reutilizada para adicionar métodos aninhados ao Ruby, que se comportariam da maneira que descrevi: eles teriam como escopo seu método de contenção, ou seja, invisível e inacessível fora de seu método de contenção corpo. E, possivelmente, eles teriam acesso ao escopo léxico do método de contenção. No entanto, se você ler a discussão que vinculei acima, poderá observar que matz é fortemente contra métodos aninhados (mas ainda para remover definições de métodos aninhados).
fonte
Na verdade, é possível. Você pode usar procs / lambda para isso.
def test(value) inner = ->() { value * value } inner.call() end
fonte
Não, não, Ruby tem métodos aninhados. Verifique isto:
def outer_method(arg) outer_variable = "y" inner_method = lambda { puts arg puts outer_variable } inner_method[] end outer_method "x" # prints "x", "y"
fonte
Você pode fazer algo assim
module Methods define_method :outer do outer_var = 1 define_method :inner do puts "defining inner" inner_var = outer_var +1 end outer_var end extend self end Methods.outer #=> defining inner #=> 1 Methods.inner #=> 2
Isso é útil quando você está fazendo coisas como escrever DSLs que requerem compartilhamento de escopo entre métodos. Mas, do contrário, é muito melhor você fazer qualquer outra coisa, porque, como as outras respostas disseram,
inner
é redefinido sempre queouter
for invocado. Se você deseja esse comportamento, e às vezes deseja, esta é uma boa maneira de obtê-lo.fonte
O jeito do Ruby é fingir com hacks confusos que farão alguns usuários se perguntarem "Como diabos isso funciona?", Enquanto os menos curiosos simplesmente memorizarão a sintaxe necessária para usar a coisa. Se você já usou Rake ou Rails, já viu esse tipo de coisa.
Aqui está um tal hack:
def mlet(name,func) my_class = (Class.new do def initialize(name,func) @name=name @func=func end def method_missing(methname, *args) puts "method_missing called on #{methname}" if methname == @name puts "Calling function #{@func}" @func.call(*args) else raise NoMethodError.new "Undefined method `#{methname}' in mlet" end end end) yield my_class.new(name,func) end
O que isso faz é definir um método de nível superior que cria uma classe e a passa para um bloco. A classe costuma
method_missing
fingir que possui um método com o nome que você escolheu. Ele "implementa" o método chamando o lambda que você deve fornecer. Ao nomear o objeto com um nome de uma letra, você pode minimizar a quantidade de digitação extra que ele requer (que é a mesma coisa que o Rails faz neleschema.rb
).mlet
é nomeado após a forma Common Lispflet
, exceto ondef
significa "função",m
significa "método".Você o usa assim:
def outer mlet :inner, ->(x) { x*2 } do |c| c.inner 12 end end
É possível fazer uma engenhoca semelhante que permite que várias funções internas sejam definidas sem aninhamento adicional, mas que requer um hack ainda mais feio do tipo que você pode encontrar na implementação de Rake ou Rspec. Descobrir como o Rspec
let!
funciona levaria você a um longo caminho para ser capaz de criar uma abominação tão horrível.fonte
:-D
Ruby tem métodos aninhados, só que eles não fazem o que você espera que façam
1.9.3p484 :001 > def kme; 'kme'; def foo; 'foo'; end; end => nil 1.9.3p484 :003 > self.methods.include? :kme => true 1.9.3p484 :004 > self.methods.include? :foo => false 1.9.3p484 :005 > kme => nil 1.9.3p484 :006 > self.methods.include? :foo => true 1.9.3p484 :007 > foo => "foo"
fonte