2011-02-01 16 views
69

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?¿Es posible tener métodos dentro de los métodos?

+1

Puede compartir el ejemplo del código o al menos un equivalente lógico de lo que está tratando de hacer. –

Respuesta

135

ACTUALIZACIÓN: Dado que esta respuesta parece haber despertado cierto interés últimamente, quería señalar que existe discussion on the Ruby issue tracker to remove the feature discussed here, namely to forbid having method definitions inside a method body.


No, Ruby no tiene métodos anidados.

se puede hacer algo como esto:

class Test1 
    def meth1 
    def meth2 
     puts "Yay" 
    end 
    meth2 
    end 
end 

Test1.new.meth1 

Pero eso es no un método anidado. Repito: Ruby no tiene métodos anidados.

Qué es esto, es una definición de método dinámico. 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, puede llamar al meth2.

¿Pero dónde se define meth2? Bueno, obviamente es no definido como un método anidado, ya que no hay no son métodos anidados en Ruby. Se define como un método de instancia de Test1:

Test1.new.meth2 
# Yay 

Además, es obvio que se volverá a definir 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 apoyo anidado métodos.

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 anidados, ya que incluso si Ruby admitía métodos anidados, no podría usar las variables del método externo en el método anidado.


ACTUALIZACIÓN CONTINUACIÓN: en una etapa más tarde, a continuación, esta sintaxis puede ser reutilizado para la adición de métodos anidados a Ruby, que se comportan de la manera que he descrito: serían el ámbito a su método que contiene, es decir, invisible e inaccesible fuera de su cuerpo de método contenedor. Y posiblemente, tendrían acceso al alcance 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 anidados (pero aún así para eliminar las definiciones de métodos anidados).

+6

También podría mostrar, sin embargo, cómo crear una lambda de cierre en un método para DRYness, o ejecutar recursión. – Phrogz

+100

Tengo la sensación de que Ruby podría no tener métodos anidados. –

+11

@ Mark Thomas: ¿Olvidé mencionar que Ruby no tiene métodos anidados? :-) En serio: en el momento en que escribí esta respuesta, ya había tres respuestas, cada una de las cuales afirmaba que Ruby * does * tenía métodos anidados. Algunas de esas respuestas incluso tenían votaciones ascendentes a pesar de estar flagrantemente equivocadas. Uno fue incluso aceptado por el OP, nuevamente, a pesar de estar equivocado. El fragmento de código que la respuesta usa para probar que Ruby admite métodos anidados, en realidad demuestra lo contrario, pero aparentemente ni los videntes ni el OP se molestaron en verificarlo. Entonces, di una respuesta correcta para cada error. :-) –

4

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" 
+4

inner_method no es un método, es una función/lambda/proc. No hay ninguna instancia asociada de ninguna clase, por lo que no es un método. –

-1

:-D

Rubí ha anidado métodos, sólo que no hacen lo que se espera de ellos a

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" 
+3

Este no es un método anidado ... ver la respuesta de * Jörg W Mittag * para una comprensión clara. – Hardik

1

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

9

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

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

La forma Ruby es que fingir con hacks confusas que tendrán algunos usuarios se pregunta "¿Cómo diablos hace esto incluso trabajar?", Mientras que los menos curioso será simplemente memorizar la sintaxis necesaria para usar la cosa. Si alguna vez has usado Rake o Rails, has visto este tipo de cosas.

Aquí es un truco como:

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 se lo pasa a un bloque. La clase usa method_missing para pretender 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 tipeo adicional que requiere (que es lo mismo que hace Rails en su schema.rb). mlet lleva el nombre del formulario de Lisp común flet, excepto donde f significa "función", m significa "método".

que lo utilice como esto:

def outer 
    mlet :inner, ->(x) { x*2 } do |c| 
    c.inner 12 
    end 
end 

Es posible hacer un artilugio similar que permite múltiples funciones internas que definirse sin anidación adicional, pero que requiere un corte aún más feo del tipo que podría encontrar en la implementación de Rake o Rspec. Entender cómo funciona el let! de Rspec te ayudaría a crear una abominación tan horrible.

Cuestiones relacionadas