¿Es posible tener métodos dentro de los métodos?

Tengo un método dentro de un método. El método interior depende de un bucle variable que se está ejecutando. ¿Es una mala idea?

ACTUALIZACIÓN: dado que esta respuesta parece haber despertado cierto interés últimamente, quería señalar que hay una discusión en el rastreador de problemas de Ruby para eliminar la característica que se discute aquí, es decir, prohibir tener definiciones de métodos dentro de un cuerpo de método .


No, Ruby no tiene métodos nesteds.

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 nested. Repito: Ruby no tiene métodos nesteds.

Lo que es esto, es una definición de método dynamic. Cuando ejecuta meth1 , se ejecutará el cuerpo de meth1 . El cuerpo simplemente define un método llamado meth2 , por lo que después de ejecutar meth1 una vez, puedes llamar a meth2 .

Pero, ¿dónde se define meth2 ? Bueno, obviamente no se define como un método nested, ya que no hay métodos nesteds 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 es compatible con los métodos nesteds.

Tenga en cuenta también que en Ruby, los cuerpos de método no pueden ser cierres, solo los cuerpos de bloque pueden hacerlo. Esto prácticamente elimina el uso principal de los métodos nesteds, ya que incluso si Ruby admitía métodos nesteds, no podía usar las variables del método externo en el método nested.


ACTUALIZACIÓN CONTINUACIÓN: en una etapa posterior , entonces, esta syntax podría ser reutilizada para agregar métodos nesteds a Ruby, que se comportaría de la manera que describí: se verían dentro de su método contenedor, es decir, invisible e inaccesible fuera de su método contenedor cuerpo. Y posiblemente, tendrían acceso al scope léxico de su método contenedor. Sin embargo, si lee la discusión que he vinculado anteriormente, puede observar que matz está muy en contra de los métodos nesteds (pero aún así para eliminar las definiciones de métodos nesteds).

En realidad es posible. Puede usar procs / lambda para esto.

 def test(value) inner = ->() { value * value } inner.call() end 

No, no, Ruby tiene métodos nesteds. 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" 

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 haces cosas como escribir DSL que requieren compartir el scope entre los métodos. Pero, de lo contrario, es mucho mejor que hagas cualquier otra cosa, porque como dicen las otras respuestas, el inner se redefine cada vez que se invoca el outer . Si quieres este comportamiento, y a veces puedes, esta es una buena forma de obtenerlo.

La manera de Ruby es falsificarla con hacks confusos que harán que algunos usuarios se pregunten “¿Cómo diablos funciona esto?”, Mientras que los menos curiosos simplemente memorizarán la syntax necesaria para usarla. Si alguna vez has usado Rake o Rails, has visto este tipo de cosas.

Aquí hay un truco como este:

 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 pretender que tiene un método con el nombre que elegiste. “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 tipeo adicional que requiere (que es lo mismo que Rails en su schema.rb ). mlet recibe su nombre del flet forma Common Lisp, excepto donde f 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 crear un artilugio similar que permita definir múltiples funciones internas sin anidamiento adicional, pero eso requiere un corte aún más feo del que podría encontrar en la implementación de Rake o Rspec. ¡Entendiendo cómo Rspec lo let! las obras te ayudarán a crear una abominación tan horrible.

:-RE

Ruby tiene métodos nesteds, solo que no hacen lo que esperas que hagan

 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"