70 votes

Pourquoi Ruby setters besoin de "soi". de qualification au sein de la classe?

Ruby setters-qu'elle soit créée par (c)attr_accessor ou manuellement-semblent être les seules méthodes qui ont besoin d' self. de qualification lors de l'accès à l'intérieur de la classe elle-même. Cela semble mettre Ruby seul le monde des langues:

  • Toutes les méthodes doivent self/this (comme Perl, et je pense que le Javascript)
  • Pas de méthodes nécessitent self/this est (C#, Java)
  • Seuls les poseurs besoin d' self/this (Ruby?)

La meilleure comparaison est C# vs Ruby, parce que les deux langues de soutien accesseur des méthodes de travail du point de vue syntaxique comme instance de classe variables: foo.x = y, y = foo.x . C# les appelle des propriétés.

Voici un exemple simple: le même programme en Ruby, 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

tenir à l'écart de l' self.qwerty =() et échec (Ruby 1.8.6 sur Linux et OS X). Maintenant 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();
  }
}

Question: Est-ce vrai? Existe-il d'autres occasions d'ailleurs setters où l'auto est-elle nécessaire? I. e., il y a des occasions où un Rubis méthode ne peut pas être invoquée sans auto?

Il y a certainement beaucoup de cas où l'auto devient nécessaire. Ce n'est pas unique à Ruby, juste pour être clair:

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
  }
}

Même ambiguïté est résolue de la même manière. Mais bien que subtile, je demande au cas où

  • Une méthode a été définie, et
  • Pas de variable locale a été défini, et

nous rencontrons

qwerty = 4

ce qui est ambigu: est-ce une invocation de méthode ou d'une nouvelle affectation de variable?


@Mike Stone

Salut! J'comprendre et d'apprécier les points que vous avez fait et de votre exemple était grande. Croyez-moi quand je dis, si j'avais assez de réputation, J'avais vote de votre réponse. Pourtant, nous avons encore pas d'accord:

  • sur une question de sémantique, et
  • un point central de fait

D'abord je demande, non sans ironie, que nous allons avoir un débat sémantique sur la du sens de "l'ambiguïté'.

Quand il s'agit de l'analyse et du langage de programmation sémantique (le sujet de cette question), vous avez sûrement admettre un large spectre de la notion 'ambiguïté'. Disons simplement adopter un hasard notation:

  1. ambigu: d'ambiguïté lexicale (lex doit "regarder de l'avant")
  2. Ambigu: grammaire de l'ambiguïté (yacc doit s'en remettre à analyser-analyse de l'arbre)
  3. AMBIGU: l'ambiguïté tout savoir au moment de l'exécution

(et il y en indésirable entre 2-3 trop). Toutes ces catégories sont résolus par la collecte de plus contextuelle info, la recherche de plus en plus à l'échelle mondiale. Ainsi, lorsque vous dire,

"qwerty = 4" est sans AMBIGUÏTÉ en C# quand il n'y a pas de variable défini...

Je ne pouvais pas être plus d'accord. Mais, en même temps, je dis

"qwerty = 4" est non-Ambigu en ruby (comme il existe maintenant)

"qwerty = 4" est Ambigu en C#

Et nous ne sommes pas encore se contredire les uns les autres. Enfin, c'est là que nous avons vraiment pas d'accord: Soit ruby pourrait ou ne pourrait pas être mis en œuvre sans plus les constructions de langage tels que,

Pour "qwerty = 4," ruby sans AMBIGUÏTÉ invoque un existant setter si il y
est pas de variable locale définie

Vous dire non. Je dis oui; un autre rubis pourrait exister, qui se comporte exactement comme le courant dans tous les domaines, à l'exception de "qwerty = 4" définit une nouvelle variable lorsque aucun set et pas de local existe, il invoque le setter si l'on existe, et il assigne à l'locale si celle-ci existe. Je suis entièrement d'accepter que je peut-être erroné. En fait, une raison pour laquelle je peux me tromper, ce serait intéressant.

Laissez-moi vous expliquer.

Imaginez que vous écrivez un nouveau langage OO avec les méthodes d'accès à la recherche comme les instances de vars (comme ruby et C#). Vous devriez probablement commencer avec conceptuel des grammaires quelque chose comme:

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

Mais l'analyseur-compilateur (même pas le moteur d'exécution) va vomir, parce que, même après tous les commentaires est grokked il n'y a aucun moyen de savoir qui de la grammaire est pertinente. Vous êtes face de laquelle un choix classique. Je ne peux pas être sûr de l'détails, mais fondamentalement, ruby fait ceci:

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

c'est pourquoi il est d'onu-Ambiguë, même si et C# fait ceci:

  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

Pour C#, "plus tard" est toujours au moment de la compilation.

Je suis sûr que ruby pourrait faire de même, mais "plus tard" devrait être au moment de l'exécution, parce que comme ben souligne que vous ne savez pas jusqu'à ce que l'instruction est exécutée, auquel cas s'applique.

Ma question n'a jamais été destiné à signifier "ai-je vraiment besoin de le 'soi'.'?" ou de "ce que une ambiguïté potentielle est évitée?" Plutôt, je voulais savoir pourquoi cette particulier choix? C'est peut-être pas la performance. Peut-être qu'il a obtenu l'emploi fait, ou il a été jugé préférable de toujours permettre un 1-liner local pour remplacer un méthode (assez rare cas exigence) ...

Mais je suis en quelque sorte ce qui suggère que les plus dynamiques de la langue pourrait être quelqu'un qui reporte cette décision la plus longue, et choisit la sémantique basé sur les plus contextuelle info: si vous n'avez pas de local et vous avez défini un setter, il devrait utiliser le setter. N'est-ce pas c'est pourquoi nous comme le rubis, smalltalk, objc, parce que l'invocation de méthode est décidé au moment de l'exécution, offrant un maximum d'expressivité?

83voto

Mike Stone Points 21293

Eh bien, je pense que la raison de ce qui est le cas c'est parce qu' qwerty = 4 est ambigu-êtes-vous la définition d'une nouvelle variable nommée qwerty ou en appelant le setter? Ruby résout cette ambiguïté en disant qu'il va créer une nouvelle variable, donc le self. est requis.

Voici un autre cas où vous avez besoin d' 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

Comme vous pouvez le voir, l'accès à l' test est ambigu, de sorte que l' self. est requis.

Aussi, c'est pourquoi l'exemple en C# est en fait pas une bonne comparaison, parce que vous définissez des variables dans une manière qui est sans équivoque de l'aide de l'incubateur. Si vous aviez défini une variable en C# qui a le même nom que l'accesseur, vous devez qualifier les appels à l'accesseur avec this., tout comme le Rubis cas.

18voto

ben Points 826

La chose importante à retenir ici est que Ruby méthodes peuvent être (un)définie en tout point, si intelligente permet de résoudre l'ambiguïté, chaque mission serait nécessaire à l'exécution de code pour vérifier si il existe une méthode avec le nom au moment de l'attribution.

17voto

Ajedi32 Points 5367

Parce que sinon, il serait impossible de définir des variables locales à l'intérieur des méthodes. variable = some_value "est ambigu. Par exemple:

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 n'était pas nécessaire pour les setters, some_method soulèverait NameError: undefined local variable or method 'some_variable'. Que-est que, la méthode fonctionne comme prévu:

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

0voto

Brian Warshaw Points 8806

Dans votre cas de test, il ne semble pas y avoir de raison de ne pas utiliser l' @ de la syntaxe à partir de l'intérieur de la classe. Votre setter n'est pas à l'exécution de toute la validation des données, donc il n'y a rien de perdu en se référant à la variable d'instance de la syntaxe.

Vous ne devez utiliser self pour les cas où vous souhaitez utiliser des données de vérification de setter, mais j'ai couru dans très peu de circonstances où j'avais besoin d'utiliser des données-vérification de la version d'une méthode à partir de mon objet. Mes chèques sont généralement mises en place pour prévenir les valeurs non valides fourni lorsque les objets externes sont à l'aide de mon interface de l'objet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X