Pero ese no es un método anidado. Repito: Ruby no tiene métodos anidados.
Esto es una definición de método dinámico. Cuando corras meth1, se ejecutará el cuerpo de meth1. El cuerpo simplemente define un método llamado meth2, por lo que después de ejecutarlo meth1una vez, puede llamar meth2.
¿Pero dónde se meth2define? Bueno, obviamente no está definido como un método anidado, ya que no hay métodos anidados en Ruby. Se define como un método de instancia de Test1:
Test1.new.meth2
# Yay
Además, obviamente se redefinirá cada vez que ejecute 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
En resumen: no, Ruby no admite métodos anidados.
Tenga en cuenta también que en Ruby, los cuerpos de método no pueden ser cierres, solo los cuerpos de bloque pueden. Esto prácticamente elimina el caso de uso principal de los métodos anidados, ya que incluso si Ruby admitiera métodos anidados, no podría usar las variables del método externo en el método anidado.
ACTUALIZACIÓN CONTINUADA: en una etapa posterior , entonces, esta sintaxis podría reutilizarse para agregar métodos anidados a Ruby, que se comportarían de la manera que describí: estarían sujetos a su método contenedor, es decir, invisibles e inaccesibles fuera de su método contenedor cuerpo. Y posiblemente, tendrían acceso al alcance léxico de su método contenedor. Sin embargo, si lee la discusión que vinculé anteriormente, puede observar que matz está muy en contra de los métodos anidados (pero aún así para eliminar las definiciones de métodos anidados).
Sin embargo, también puede mostrar cómo crear una lambda de cierre en un método para DRYness o ejecutar la recursividad.
Phrogz
119
Tengo la sensación de que Ruby podría no tener métodos anidados.
Mark Thomas
16
@Mark Thomas: ¿Olvidé mencionar que Ruby no tiene métodos anidados? :-) En serio: en el momento de escribir esta respuesta, ya había tres respuestas, cada uno de los cuales afirmaban que Ruby hace tener métodos anidados. Algunas de esas respuestas incluso obtuvieron votos positivos a pesar de estar descaradamente equivocadas. Uno incluso fue aceptado por el OP, nuevamente, a pesar de estar equivocado. El fragmento de código que usa la respuesta para demostrar que Ruby admite métodos anidados, en realidad demuestra lo contrario, pero aparentemente ni los votantes positivos ni el OP se molestaron en verificar. Entonces, di una respuesta correcta para cada respuesta incorrecta. :-)
Jörg W Mittag
10
Cuando se da cuenta de que todas estas son solo instrucciones para el Kernel que modifican tablas y que los métodos, clases y módulos son solo entradas en tablas y no son realmente reales, se vuelve como Neo cuando ve cómo se ve Matrix. Entonces podría volverse realmente filosófico y decir que además de los métodos anidados, ni siquiera hay métodos. Ni siquiera hay agentes. Son programas en la matriz. Incluso ese bistec jugoso que estás comiendo es solo una entrada en una mesa.
mydoghasworms
3
No hay métodos, tu código es solo una simulación en The Matrix
bbozo
13
De hecho, es posible. Puede usar procs / lambda para esto.
deftest(value)
inner = ->() {
value * value
}
inner.call()
end
No se equivoca, pero su respuesta está redactada como una solución para lograr métodos anidados. Cuando en realidad solo estás usando procs que no son métodos. Es una buena respuesta fuera de afirmar que resuelve "métodos anidados"
Brandon Buck
5
No, no, Ruby tiene métodos anidados. Mira esto:
defouter_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x"# prints "x", "y"
Esto es útil cuando está haciendo cosas como escribir DSL que requieren compartir el alcance entre métodos. Pero de lo contrario, es mucho mejor hacer cualquier otra cosa, porque, como dicen las otras respuestas, innerse redefine cada vez que outerse invoca. Si desea este comportamiento, y a veces puede hacerlo, esta es una buena manera de obtenerlo.
La forma de Ruby es falsificarlo con trucos confusos que harán que algunos usuarios se pregunten "¿Cómo diablos funciona esto?", Mientras que los menos curiosos simplemente memorizarán la sintaxis necesaria para usar la cosa. Si alguna vez ha usado Rake o Rails, ha visto este tipo de cosas.
Aquí hay un truco de este tipo:
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
Lo que hace es definir un método de nivel superior que crea una clase y la pasa a un bloque. La clase usa method_missingpara fingir que tiene un método con el nombre que eligió. "Implementa" el método llamando a la lambda que debe proporcionar. Al nombrar el objeto con un nombre de una letra, puede minimizar la cantidad de escritura adicional que requiere (que es lo mismo que hace Rails en su schema.rb). mletlleva el nombre de la forma Common Lisp flet, excepto donde fsignifica "función", msignifica "método".
Es posible hacer un artilugio similar que permita definir múltiples funciones internas sin anidamiento adicional, pero eso requiere un truco aún más feo del tipo que puede encontrar en la implementación de Rake o Rspec. Averiguar cómo funciona Rspec te let!ayudaría mucho a crear una abominación tan horrible.
Respuestas:
ACTUALIZACIÓN: dado que esta respuesta parece haber despertado cierto interés últimamente, quería señalar que existe una discusión sobre el rastreador de problemas de Ruby para eliminar la característica discutida aquí, es decir, prohibir tener definiciones de método dentro de un cuerpo de método .
No, Ruby no tiene métodos anidados.
Puedes hacer algo como esto:
class Test1 def meth1 def meth2 puts "Yay" end meth2 end end Test1.new.meth1
Pero ese no es un método anidado. Repito: Ruby no tiene métodos anidados.
Esto es una definición de método dinámico. Cuando corras
meth1
, se ejecutará el cuerpo demeth1
. El cuerpo simplemente define un método llamadometh2
, por lo que después de ejecutarlometh1
una vez, puede llamarmeth2
.¿Pero dónde se
meth2
define? Bueno, obviamente no está definido como un método anidado, ya que no hay métodos anidados en Ruby. Se define como un método de instancia deTest1
:Test1.new.meth2 # Yay
Además, obviamente se redefinirá cada vez que ejecute
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
En resumen: no, Ruby no admite métodos anidados.
Tenga en cuenta también que en Ruby, los cuerpos de método no pueden ser cierres, solo los cuerpos de bloque pueden. Esto prácticamente elimina el caso de uso principal de los métodos anidados, ya que incluso si Ruby admitiera métodos anidados, no podría usar las variables del método externo en el método anidado.
ACTUALIZACIÓN CONTINUADA: en una etapa posterior , entonces, esta sintaxis podría reutilizarse para agregar métodos anidados a Ruby, que se comportarían de la manera que describí: estarían sujetos a su método contenedor, es decir, invisibles e inaccesibles fuera de su método contenedor cuerpo. Y posiblemente, tendrían acceso al alcance léxico de su método contenedor. Sin embargo, si lee la discusión que vinculé anteriormente, puede observar que matz está muy en contra de los métodos anidados (pero aún así para eliminar las definiciones de métodos anidados).
fuente
De hecho, es posible. Puede usar procs / lambda para esto.
def test(value) inner = ->() { value * value } inner.call() end
fuente
No, no, Ruby tiene métodos anidados. Mira esto:
def outer_method(arg) outer_variable = "y" inner_method = lambda { puts arg puts outer_variable } inner_method[] end outer_method "x" # prints "x", "y"
fuente
Puedes hacer algo como esto
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
Esto es útil cuando está haciendo cosas como escribir DSL que requieren compartir el alcance entre métodos. Pero de lo contrario, es mucho mejor hacer cualquier otra cosa, porque, como dicen las otras respuestas,
inner
se redefine cada vez queouter
se invoca. Si desea este comportamiento, y a veces puede hacerlo, esta es una buena manera de obtenerlo.fuente
La forma de Ruby es falsificarlo con trucos confusos que harán que algunos usuarios se pregunten "¿Cómo diablos funciona esto?", Mientras que los menos curiosos simplemente memorizarán la sintaxis necesaria para usar la cosa. Si alguna vez ha usado Rake o Rails, ha visto este tipo de cosas.
Aquí hay un truco de este tipo:
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
Lo que hace es definir un método de nivel superior que crea una clase y la pasa a un bloque. La clase usa
method_missing
para fingir que tiene un método con el nombre que eligió. "Implementa" el método llamando a la lambda que debe proporcionar. Al nombrar el objeto con un nombre de una letra, puede minimizar la cantidad de escritura adicional que requiere (que es lo mismo que hace Rails en suschema.rb
).mlet
lleva el nombre de la forma Common Lispflet
, excepto dondef
significa "función",m
significa "método".Lo usas así:
def outer mlet :inner, ->(x) { x*2 } do |c| c.inner 12 end end
Es posible hacer un artilugio similar que permita definir múltiples funciones internas sin anidamiento adicional, pero eso requiere un truco aún más feo del tipo que puede encontrar en la implementación de Rake o Rspec. Averiguar cómo funciona Rspec te
let!
ayudaría mucho a crear una abominación tan horrible.fuente
:-RE
Ruby tiene métodos anidados, solo que no hacen lo que esperarías que hicieran
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"
fuente