2008-09-04 11 views
59

Los usuarios de Ruby, ya sean creados por (c)attr_accessor o de forma manual, parecen ser los únicos métodos que necesitan la calificación self. cuando se accede a ella dentro de la clase. Esto parece poner Rubí solo el mundo de los idiomas:¿Por qué los setters de Ruby necesitan "self"? calificación dentro de la clase?

  • Todos los métodos necesitan self/this (como Perl, y creo que Javascript)
  • No hay métodos requieren self/this es (C#, Java)
  • Sólo los emisores necesitan self/this (Ruby?)

La mejor comparación es C# y Ruby, porque ambos idiomas admiten métodos de acceso que funcionan sintácticamente igual que insta clase nce variables: foo.x = y, y = foo.x. C# los llama propiedades.

Aquí hay un ejemplo simple; el mismo programa en Ruby entonces C#:

class A 
    def qwerty; @q; end     # manual getter 
    def qwerty=(value); @q = value; end # manual setter, but attr_accessor is same 
    def asdf; self.qwerty = 4; end  # "self." is necessary in ruby? 
    def xxx; asdf; end     # we can invoke nonsetters w/o "self." 
    def dump; puts "qwerty = #{qwerty}"; end 
end 

a = A.new 
a.xxx 
a.dump 

quita el self.qwerty =() y falla (Rubí 1.8.6 en Linux & OS X). Ahora C#:

using System; 

public class A { 
    public A() {} 
    int q; 
    public int qwerty { 
    get { return q; } 
    set { q = value; } 
    } 
    public void asdf() { qwerty = 4; } // C# setters work w/o "this." 
    public void xxx() { asdf(); }  // are just like other methods 
    public void dump() { Console.WriteLine("qwerty = {0}", qwerty); } 
} 

public class Test { 
    public static void Main() { 
    A a = new A(); 
    a.xxx(); 
    a.dump(); 
    } 
} 

Pregunta: ¿Es esto verdad? ¿Hay otras ocasiones además de instaladores donde el yo es necesario? Es decir, ¿hay otras ocasiones en que un método de Ruby no se pueda invocarsin?

Ciertamente hay muchos casos en que el auto se convierte en necesario. Esto no es aplicable sólo a Ruby, para ser claros:

using System; 

public class A { 
    public A() {} 
    public int test { get { return 4; }} 
    public int useVariable() { 
    int test = 5; 
    return test; 
    } 
    public int useMethod() { 
    int test = 5; 
    return this.test; 
    } 
} 

public class Test { 
    public static void Main() { 
    A a = new A(); 
    Console.WriteLine("{0}", a.useVariable()); // prints 5 
    Console.WriteLine("{0}", a.useMethod()); // prints 4 
    } 
} 

misma ambigüedad se resuelve de la misma manera. Sin embargo, aunque sutil que estoy preguntando por el caso en el que

  • Un método tiene ha definido, y
  • Sin variable local se ha definido, y

nos encontramos con

qwerty = 4 

que es ambiguo: ¿se trata de una invocación a un método o una nueva asignación de variable local?


@ Mike Piedra

Hola! Entiendo y aprecio los puntos que ha hecho y su ejemplo fue excelente. Créanme cuando digo, si tuviera suficiente reputación, votaría su respuesta.Sin embargo, aún en desacuerdo:

  • en una cuestión de semántica, y
  • en un punto central de hecho

primer reclamo, no sin ironía, estamos teniendo un debate semántico sobre la significado de 'ambigüedad'.

Cuando se trata de la semántica del lenguaje de programación y programación (el asunto de esta pregunta), seguramente admitiría un amplio espectro de la noción 'ambigüedad'. Vamos a adoptar alguna notación azar:

  1. ambigua: léxica ambigüedad (lex debe 'mirar hacia adelante')
  2. ambiguo: gramatical ambigüedad (yacc debe ceder ante el análisis árbol de análisis sintáctico)
  3. ambigua: la ambigüedad saber todo en el momento de la ejecución

(y también hay basura entre 2-3). Todas estas categorías se resuelven por reuniendo más información contextual, mirando cada vez más globalmente. Así que cuando se decir,

"qwerty = 4" es inequívoco en C# cuando no hay ninguna variable definida ...

no podía estar más de acuerdo. Pero por la misma razón, estoy diciendo

"qwerty = 4" es no-ambiguo en rubí (tal como existe ahora)

"qwerty = 4" es ambiguo en C#

Y aún no nos estamos contradiciendo. Por último, aquí es donde realmente en desacuerdo: Cualquiera de rubí podría o no podría ser implementado sin más construcciones del lenguaje de tal manera que,

Por "qwerty = 4," rubí sin ambigüedades invoca un colocador existente si hay
no hay una variable local definida

Usted dice que no. Yo digo si; puede existir otro ruby ​​que se comporte exactamente como la corriente en todos los sentidos, excepto "qwerty = 4" define una nueva variable cuando no existe el establecimiento y no existe local, invoca al ajustador si existe un y lo asigna al local si existe. Acepto completamente que I podría estar equivocado. De hecho, una razón por la que podría estar equivocado sería interesante.

Déjame explicarte.

Imagine que está escribiendo un nuevo lenguaje OO con métodos de acceso buscando como instancias vars (como ruby ​​& C#).Es probable que empieces con gramáticas conceptuales algo como:

var = expr // assignment 
    method = expr // setter method invocation 

Pero el analizador-compilador (ni siquiera el tiempo de ejecución) se vomitar, porque incluso después de toda la entrada se asimiló no hay manera de saber lo que la gramática es pertinente. Te enfrentas a una elección clásica. No puedo estar seguro de los detalles, pero básicamente rubí hace esto:

var = expr // assignment (new or existing) 
    // method = expr, disallow setter method invocation without . 

que es por eso que es no-ambiguo, mientras que y C# hace esto:

symbol = expr // push 'symbol=' onto parse tree and decide later 
       // if local variable is def'd somewhere in scope: assignment 
       // else if a setter is def'd in scope: invocation 

para C#, 'más tarde 'todavía está en tiempo de compilación.

Estoy seguro de que ruby ​​podría hacer lo mismo, pero 'later' tendría que ser en tiempo de ejecución, porque como ben señala que no se sabe hasta que se ejecute la sentencia que se aplica el caso .

Mi pregunta nunca tuvo la intención de decir "¿realmente necesito el 'yo'?" o "¿qué se está evitando la posible ambigüedad?" Más bien, quería saber por qué se hizo esta elección particular de ? Tal vez no es el rendimiento. Es posible que simplemente consiguió el trabajo hecho, o se consideró más adecuado para permitir siempre una 1-liner local para reemplazar un método (un requisito caso bastante raro) ...

Pero Soy una especie de lo que sugiere que la el lenguaje más dinámico puede ser el que pospone esta decisión por más tiempo, y elige la semántica basada en la información más contextual : así que si no tiene local y definió un setter, usaría el setter. ¿No es por lo que nos gusta ruby, smalltalk, objc, porque la invocación al método se decide en tiempo de ejecución, ofreciendo máxima expresividad?

+0

PHP también requiere '$ this->' al acceder a variables de instancias. Eso me hace volar todo el tiempo. – Chloe

+0

Un receptor explícito solo es necesario para los métodos de clase, no para los métodos de instancia. –

+0

Acepto - Tampoco me gusta este método para resolver el amibuity. Viola el principio de la menor sorpresa. – Dogweather

Respuesta

17

Lo importante a recordar aquí es que los métodos de Ruby pueden ser (no) definidos en cualquier punto, por lo que para resolver la ambigüedad de forma inteligente, cada asignación debería ejecutar código para verificar si hay un método con el asignado nombre en el momento de la asignación.

+13

Esto no es correcto, la verdadera razón es que simplemente indicando 'x = 5' instancia una variable local' x' que anula cualquier setter existente 'self.x ='. Para resolver esta ambigüedad, si desea llamar a setter 'x =', debe indicar explícitamente lo que desea hacer indicando 'self.x =' – bbozo

75

Bueno, creo que la razón por la cual este es el caso es porque qwerty = 4 es ambiguo: ¿está definiendo una nueva variable llamada qwerty o llamando al setter? Ruby resuelve esta ambigüedad diciendo que creará una nueva variable, por lo tanto, se requiere self..

Aquí es otro caso en el que necesita self.:

class A 
    def test 
    4 
    end 
    def use_variable 
    test = 5 
    test 
    end 
    def use_method 
    test = 5 
    self.test 
    end 
end 
a = A.new 
a.use_variable # returns 5 
a.use_method # returns 4 

Como se puede ver, el acceso a test es ambigua, por lo que se requiere la self..

Además, esta es la razón por la cual el ejemplo de C# no es realmente una buena comparación, porque usted define variables de una manera que no es ambigua desde el uso del setter. Si había definido una variable en C# que tenía el mismo nombre que el descriptor de acceso, debería calificar las llamadas al descriptor de acceso con this., al igual que en el caso de Ruby.

+0

Esta explicación es muy buena –

13

Porque de lo contrario sería imposible establecer variables locales dentro de los métodos. variable = some_value es ambiguo.Por ejemplo:

class ExampleClass 
    attr_reader :last_set 
    def method_missing(name, *args) 
    if name.to_s =~ /=$/ 
     @last_set = args.first 
    else 
     super 
    end 
    end 

    def some_method 
    some_variable = 5 # Set a local variable? Or call method_missing? 
    puts some_variable 
    end 
end 

Si self no era necesario para los emisores, some_method elevaría NameError: undefined local variable or method 'some_variable'. Sin embargo, el método funciona como se esperaba:

example = ExampleClass.new 
example.blah = 'Some text' 
example.last_set #=> "Some text" 
example.some_method # prints "5" 
example.last_set #=> "Some text" 
+6

Haha. Estaba a punto de votar esta respuesta, y luego me di cuenta de que era mía de hace un año. ;-) – Ajedi32

+0

Creo que esta es la única respuesta que establece claramente por qué no puedes irte sin 'yo'. – amoebe

+0

Debo añadir que la razón por la cual Java y sus similares se salgan con la suya, es porque como lenguajes tipados estáticamente, el compilador puede determinar de antemano si va a haber un conflicto y generar un error apropiado en consecuencia. Los lenguajes dinámicos (lenguajes de scripting), de tipo débil (php) y de tipo pato (ruby/python) no tienen ese lujo, ¡ay! – Shayne

Cuestiones relacionadas