web-dev-qa-db-fra.com

Pourquoi un appel de fonction requiert-il le nom du paramètre dans Swift?

J'ai cette fonction dans une classe:

func multiply(factor1:Int, factor2:Int) -> Int{
    return factor1 * factor2
}

J'essaie d'appeler la fonction en utilisant ceci:

var multResult = calculator.multiply(9834, 2321)

Le problème est que le compilateur veut que cela ressemble plus à ceci:

var multResult = calculator.multiply(9834, factor2: 2321)

Pourquoi le premier cause-t-il une erreur? 

65
67cherries

Mise à jour pour Swift 2.0: les fonctions se comportent désormais de manière identique aux méthodes, et par défaut pour les deux:

  • le premier paramètre n'a pas de nom externe; et
  • les autres paramètres ont un nom externe identique au nom interne.

En dehors de cela, les règles ci-dessous s'appliquent toujours, à l'exception du fait que la syntaxe abrégée # a maintenant disparu.


Voici une réponse plus générale: les fonctions se comportent différemment lorsqu'elles sont définies en tant que fonctions vraies en dehors d'une classe et en tant que méthodes. De plus, les méthodes init ont une règle spéciale.


Les fonctions

Supposons que vous définissiez ceci:

func multiply1(f1: Double, f2: Double) -> Double {
    return f1 * f2
}

Les noms de paramètres sont ici uniquement local à la fonction, et ne peuvent pas être utilisés lors de l'appel de la fonction:

multiply1(10.0, 10.0)

Si vous voulez forcer l'utilisation de paramètres nommés lors de l'appel de la fonction, vous pouvez. Préfixez chaque déclaration de paramètre avec son nom externe. Ici, le nom externe de f1 est f1param, et pour f2, nous utilisons le raccourci où nous le préfixons par # pour indiquer que le nom local doit également être utilisé comme nom externe:

func multiply2(f1param f1: Double, #f2: Double) -> Double {
    return f1 * f2
}

Ensuite, les paramètres nommés doivent être utilisés:

multiply2(f1param: 10.0, f2: 10.0)

Les méthodes

Les choses sont différentes pour les méthodes. Par défaut, tous les paramètres sauf le premier sont nommés, comme vous l'avez découvert. Supposons que nous ayons ceci et considérons la méthode multiply1:

class Calc {
    func multiply1(f1: Double, f2: Double) -> Double {
        return f1 * f2
    }
    func multiply2(f1param f1: Double, f2: Double) -> Double {
        return f1 * f2
    }
    func multiply3(f1: Double, _ f2: Double) -> Double {
        return f1 * f2
    }
}

Ensuite, vous devez utiliser le nom du deuxième paramètre (et le cas échéant suivant):

let calc = Calc()
calc.multiply1(1.0, f2: 10.0)

Vous pouvez forcer l'utilisation d'un paramètre nommé pour le premier argument en lui attribuant un nom externe, comme pour les fonctions (ou en préfixant son nom local avec # si vous souhaitez utiliser le même nom externe que son nom local). Ensuite, vous devez l'utiliser:

calc.multiply2(f1param: 10.0, f2: 10.0)

Enfin, vous pouvez déclarer un nom externe de _ pour les autres arguments suivants, indiquant que vous souhaitez appeler votre méthode sans utiliser de paramètres nommés, comme ceci:

calc.multiply3(10.0, 10.0)

Note d'interopérabilité: Si vous préfixez class Calc avec l'annotation @objc, vous pouvez l'utiliser à partir du code Objective-C, ce qui équivaut à cette déclaration (regardez les noms des paramètres):

@interface Calc
- (double)multiply1:(double)f1 f2:(double)f2;
- (double)multiply2WithF1param:(double)f1 f2:(double)f2;
- (double)multiply3:(double)f1 :(double)f2;
@end

Méthodes Init

La règle diffère un peu pour les méthodes init, où tous les paramètres ont un nom externe par défaut. Par exemple, cela fonctionne:

class Calc {
    init(start: Int) {}
    init(_ start: String) {}
}

let c1 = Calc(start: 6)
let c2 = Calc("6")

Ici, vous devez spécifier start: pour la surcharge qui accepte une Int, mais vous devez l'omettre pour la surcharge qui accepte une String.

Note d'interopérabilité: cette classe serait exportée vers Objective-C comme ceci:

@interface Calc
- (instancetype)initWithStart:(NSInteger)start __attribute__((objc_designated_initializer));
- (instancetype)init:(NSString *)start __attribute__((objc_designated_initializer));
@end

Fermetures

Supposons que vous définissiez un type de fermeture comme ceci:

typealias FancyFunction = (f1: Double, f2: Double) -> Double

Les noms de paramètres se comporteront de manière très similaire à ceux d'une méthode. Vous devrez fournir les noms aux paramètres lors de l'appel de la fermeture, sauf si vous définissez explicitement le nom externe sur _.

Par exemple, en exécutant la fermeture:

fund doSomethingInteresting(withFunction: FancyFunction) {
    withFunction(f1: 1.0, f2: 3.0)
}

En règle générale: même si vous ne les aimez pas, vous devriez probablement essayer de continuer à utiliser des paramètres nommés au moins chaque fois que deux paramètres ont le même type, afin de les lever de l'ambiguïté. Je dirais également qu'il est bon de nommer également au moins tous les paramètres Int et Boolean.

116

Les noms de paramètre de l'appel de fonction sont appelés noms de mot-clé et leurs racines sont remontées dans le langage Smalltalk.

Les classes et les objets sont souvent réutilisés ailleurs, ou font partie de très grands systèmes complexes et ne feront pas l'objet d'une maintenance active pendant de longues périodes.

Améliorer la clarté et la lisibilité du code est très important dans ces situations, car le code devient souvent la seule documentation disponible lorsque les développeurs sont soumis à des contraintes de délai.

Attribuer à chaque paramètre un nom de mot clé descriptif permet aux responsables de la maintenance de voir rapidement le but d'un appel de fonction en jetant un coup d'œil sur l'appel de fonction, par opposition à une analyse plus approfondie du code de fonction lui-même. Cela rend explicite la signification implicite des paramètres.

Le dernier langage qui a adopté les noms de mots clés pour les paramètres dans les appels de fonction est Rust (lien) - décrit comme "un langage de programmation système extrêmement rapide, qui empêche les erreurs de segmentation et garantit la sécurité des threads".

Les systèmes à disponibilité élevée nécessitent une meilleure qualité de code. Les noms de mots-clés offrent aux équipes de développement et de maintenance davantage de possibilités d’éviter que et attrapent les erreurs lorsqu’ils envoient un paramètre incorrect ou ne les appellent pas dans le désordre.

Ils peuvent être verbeux ou laconiques, mais les Smalltalkers préfèrent les mots verbeux et descriptifs aux lacustres et sans signification. Ils peuvent se permettre, car leur IDE se chargera de ce type de frappe pour eux.

4
Euan M

puisque vous avez utilisé calculator.multiply() dans l'exemple de code, je suppose que cette fonction est une méthode de l'objet calculator.

Swift hérite de beaucoup d'objectifs de objective-c et en voici un exemple:

Quand dans objectif-c vous feriez (hypothétiquement):

[calculator multiply:@9834 factor2:@2321];

l'équivalent dans Swift est:

calculator.multiply(9834, factor2:2321);
2
Jiaaro

Parce que votre fonction "multiplier" est une méthode, et comme Objective-c, les paramètres des méthodes font partie du nom.

Par exemple, vous pouvez le faire.

class Calculator {

    func multiply(factor1:Int, factor2:Int) -> Int{
        return factor1 * factor2
    }

    func multiply(factor1:Int, factor2:Int, factor3:Int) -> Int{
        return factor1 * factor2 * factor3
    }

}

Ici, il existe deux méthodes différentes, avec des noms différents, multiplier (facteur2) et multiplier (facteur2 facteur3).

Cette règle s'applique uniquement aux méthodes, si vous déclarez cela comme une fonction en dehors d'une classe, l'appel de fonction ne nécessite pas de nom de paramètre.

1
Daniel

Une note sur la transmission d'une méthode sous forme d'argument qui ne renvoie aucune valeur: 

func refresh(obj:Obj, _ method: (Obj)->Void = setValue) {
    method(element)
}
func setValue(obj:Obj){
    obj.value = "someValue"
}
refresh(someObj,setValue)
0
eonist

La raison est historique. C'est comme ça que ça a marché dans Smalltalk et ça a survécu dans ses descendants. Squeak, Scratch , Blockly , Objective C et Swift.

Les langages rigolos (Squeak, Scratch et Blockly) s’y tiennent, car les programmeurs débutants ont tendance à avoir des problèmes d’arité et de ordre des paramètres. C'était la raison initiale pour laquelle Smalltalk l'a fait de cette façon. Je ne sais pas pourquoi ObjC et Swift ont décidé d'adopter la convention, mais ils l'ont fait.

 Scratch example program

0
user7610