web-dev-qa-db-fra.com

Pourquoi Ruby ne prend-il pas en charge la méthode surchargée?

Au lieu de supporter la surcharge de méthode, Ruby écrase les méthodes existantes. Quelqu'un peut-il expliquer pourquoi le langage a été conçu de cette façon?

134
Rambo

La surcharge de méthodes peut être obtenue en déclarant deux méthodes avec le même nom et des signatures différentes. Ces différentes signatures peuvent être soit,

  1. Arguments avec différents types de données, par exemple: method(int a, int b) vs method(String a, String b)
  2. Nombre variable d'arguments, par exemple: method(a) vs method(a, b)

La première méthode ne permet pas de surcharger une méthode car il n’ya pas de déclaration de type de données en Ruby ( dynamic typed language ). Donc, la seule façon de définir la méthode ci-dessus est def(a,b)

Avec la deuxième option, il peut sembler que nous pouvons parvenir à une surcharge de méthode, mais nous ne pouvons pas. Disons que j'ai deux méthodes avec un nombre différent d'arguments,

def method(a); end;
def method(a, b = true); end; # second argument has a default value

method(10)
# Now the method call can match the first one as well as the second one, 
# so here is the problem.

Ainsi, Ruby doit conserver une méthode dans la chaîne de recherche de méthode avec un nom unique.

148
nkm

"Surcharger" est un terme qui n'a tout simplement pas de sens en Ruby. C'est fondamentalement un synonyme de "dispatch statique à base d'arguments", mais Ruby ne ---) a (= dispatch du tout. Donc, la raison pour laquelle Ruby ne prend pas en charge la répartition statique basée sur les arguments, c'est parce qu'elle ne prend pas en charge la répartition statique, point. Il ne supporte pas l'envoi statique de tout type, qu'il soit basé sur des arguments ou autrement.

Maintenant, si not vous posez précisément des questions sur la surcharge, mais peut-être sur dynamique une répartition basée sur des arguments, la réponse est: car Matz ne l’a pas mis en œuvre. Parce que personne d'autre n'a pris la peine de le proposer. Parce que personne d'autre n'a pris la peine de l'appliquer.

En général, la répartition dynamique basée sur les arguments dans une langue avec des arguments facultatifs et des listes d'arguments de longueur variable est très difficile à obtenir correctement et même plus difficile à le rendre compréhensible. Même dans les langages avec une répartition basée sur des arguments statique et sans arguments optionnels (comme Java, par exemple), il est parfois presque impossible de dire pour un simple mortel, qui surcharge est va être choisi.

En C #, vous pouvez réellement encoder n'importe lequel problème de 3-SAT en résolution de surcharge, ce qui signifie que la résolution de surcharge en C # est NP-difficile.

Essayez maintenant cela avec dynamique dispatch, où vous avez la dimension temporelle supplémentaire à garder dans votre tête.

Il existe des langages qui envoient de manière dynamique en fonction de tous les arguments d'une procédure, par opposition aux langages orientés objet, qui envoient uniquement sur l'argument zeroth self "caché". LISP commun, par exemple, distribue sur les types dynamiques et même les valeurs dynamiques de tous les arguments. Clojure distribue une fonction arbitraire de tous les arguments (BTW est extrêmement cool et extrêmement puissant).

Mais je ne connais pas de langage OO avec une répartition dynamique basée sur des arguments. Martin Odersky a déclaré qu'il pourrait envisager d'ajouter une dépêche argumentée à Scala, mais seulement s'il peut supprimer la surcharge en même temps et être rétrogradé -compatible à la fois avec le code Scala existant qui utilise la surcharge et compatible avec Java (il a notamment mentionné Swing et AWT qui jouent des tours extrêmement complexes exerçant à peu près tous les cas de coins sombres de Java règles de surcharge complexes). J'ai moi-même eu quelques idées sur l'ajout d'une répartition basée sur les arguments à Ruby, mais je n'ai jamais trouvé comment le faire de manière rétro-compatible.

202
Jörg W Mittag

Je suppose que vous recherchez la possibilité de le faire:

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end

Ruby soutient cela d'une manière différente:

def my_method(*args)
  if args.length == 1
    #method 1
  else
    #method 2
  end
end

Un modèle courant consiste également à transmettre les options sous forme de hachage:

def my_method(options)
    if options[:arg1] and options[:arg2]
      #method 2
    elsif options[:arg1]
      #method 1
    end
end

my_method arg1: 'hello', arg2: 'world'

J'espère que cela pourra aider

79
Derek Ekins

La surcharge de méthode est logique dans un langage avec typage statique, où vous pouvez distinguer différents types d'arguments

f(1)
f('foo')
f(true)

ainsi qu'entre différents nombres d'arguments

f(1)
f(1, 'foo')
f(1, 'foo', true)

La première distinction n'existe pas en Ruby. Ruby utilise la frappe dynamique ou "frappe de canard". La deuxième distinction peut être gérée par des arguments par défaut ou en utilisant des arguments:

def f(n, s = 'foo', flux_compensator = true)
   ...
end


def f(*args)
  case args.size
  when  
     ...
  when 2
    ...
  when 3
    ...
  end
end
9
bjelli

Cela ne répond pas à la question de savoir pourquoi Ruby n'a pas de surcharge de méthodes, mais que des bibliothèques tierces peuvent le fournir.

La bibliothèque contracts.Ruby permet la surcharge. Exemple adapté du tutoriel:

class Factorial
  include Contracts

  Contract 1 => 1
  def fact(x)
    x
  end

  Contract Num => Num
  def fact(x)
    x * fact(x - 1)
  end
end

# try it out
Factorial.new.fact(5)  # => 120

Notez que ceci est en réalité plus puissant que la surcharge de Java, car vous pouvez spécifier des valeurs à comparer (par exemple, 1), pas simplement des types.

Vous constaterez une diminution des performances en utilisant ceci cependant; vous devrez exécuter des points de repère pour décider combien vous pouvez tolérer.

7
Kelvin

Je fais souvent la structure suivante:

def method(param)
    case param
    when String
         method_for_String(param)
    when Type1
         method_for_Type1(param)

    ...

    else
         #default implementation
    end
end

Ceci permet à l'utilisateur de l'objet d'utiliser le nom_méthode propre et clair: méthode Mais s'il souhaite optimiser l'exécution, il peut directement appeler la méthode correcte.

En outre, cela rend vos tests plus clairs et meilleurs.

1
Dam

il y a déjà d'excellentes réponses sur le côté pourquoi de la question. Cependant, si vous recherchez d'autres solutions, utilisez la commande fonctionnaliste gemme inspirée d'Elixir la recherche de motifs .

 classe Foo 
 include Functional :: PatternMatching 

 ## Constructor Over loading 
 defn (: initialize) {@name = 'baz'} 
 defn (: initialize, _) {| nom | @nom = nom.to_s} 

 ## Surcharge de méthode 
 defn (: greet,: male) {
 met "Bonjour, monsieur!" 
 } 

 defn (: greet,: female) {
 met "Bonjour, madame!" 
 } 
 fin

 foo = Foo.new ou Foo.new ('Bar') 
 foo.greet (: male) => "Bonjour, monsieur!" 
 foo.greet (: female) => "Bonjour, madame!" 
0
Oshanz