Pourquoi ai-je besoin de traits de soulignement dans Swift?

161

Ici, il dit: "Remarque: le _moyen" Je me fiche de cette valeur "", mais venant de JavaScript, je ne comprends pas ce que cela signifie.

La seule façon d'obtenir ces fonctions à imprimer était d'utiliser les traits de soulignement avant les paramètres:

func divmod(_ a: Int, _ b:Int) -> (Int, Int) {
    return (a / b, a % b)
}

print(divmod(7, 3))
print(divmod(5, 2))
print(divmod(12,4))

Sans les traits de soulignement, je dois l'écrire comme ceci pour éviter toute erreur:

func divmod(a: Int, b:Int) -> (Int, Int) {
    return (a / b, a % b)
}

print(divmod(a: 7, b: 3))
print(divmod(a: 5, b: 2))
print(divmod(a: 12,b: 4))

Je ne comprends pas cet usage de soulignement. Quand, comment et pourquoi utiliser ces traits de soulignement?

Michael Rader
la source

Réponses:

354

Il y a quelques nuances dans différents cas d'utilisation, mais généralement un trait de soulignement signifie «ignorer cela».


Lors de la déclaration d'une nouvelle fonction, un trait de soulignement indique à Swift que le paramètre ne doit pas avoir d'étiquette lorsqu'il est appelé - c'est le cas que vous voyez. Une déclaration de fonction plus complète ressemble à ceci:

func myFunc(label name: Int) // call it like myFunc(label: 3)

"label" est une étiquette d'argument et doit être présente lorsque vous appelez la fonction. (Et depuis Swift 3, les étiquettes sont requises pour tous les arguments par défaut.) "Nom" est le nom de la variable pour cet argument que vous utilisez dans la fonction. Un formulaire plus court ressemble à ceci:

func myFunc(name: Int) // call it like myFunc(name: 3)

Il s'agit d'un raccourci qui vous permet d'utiliser le même mot pour l'étiquette d'argument externe et le nom du paramètre interne. C'est équivalent à func myFunc(name name: Int).

Si vous souhaitez que votre fonction puisse être appelée sans étiquettes de paramètres, vous utilisez le trait _de soulignement pour que l'étiquette ne soit rien / ignorée. (Dans ce cas, vous devez fournir un nom interne si vous souhaitez pouvoir utiliser le paramètre.)

func myFunc(_ name: Int) // call it like myFunc(3)

Dans une instruction d'affectation, un trait de soulignement signifie "ne pas affecter à quoi que ce soit". Vous pouvez l'utiliser si vous souhaitez appeler une fonction qui renvoie un résultat mais ne se soucie pas de la valeur renvoyée.

_ = someFunction()

Ou, comme dans l'article auquel vous avez lié, pour ignorer un élément d'un tuple retourné:

let (x, _) = someFunctionThatReturnsXandY()

Lorsque vous écrivez une fermeture qui implémente un type de fonction défini, vous pouvez utiliser le trait de soulignement pour ignorer certains paramètres.

PHPhotoLibrary.performChanges( { /* some changes */ },
    completionHandler: { success, _ in // don't care about error
        if success { print("yay") }
    })

De même, lors de la déclaration d'une fonction qui adopte un protocole ou remplace une méthode de superclasse, vous pouvez utiliser _pour les noms de paramètres pour ignorer les paramètres. Étant donné que le protocole / la superclasse peut également définir que le paramètre n'a pas d'étiquette, vous pouvez même vous retrouver avec deux traits de soulignement à la suite.

class MyView: NSView {
    override func mouseDown(with _: NSEvent) {
        // don't care about event, do same thing for every mouse down
    }
    override func draw(_ _: NSRect) {
        // don't care about dirty rect, always redraw the whole view
    }
}

Un peu lié aux deux derniers styles: lorsque vous utilisez une construction de contrôle de flux qui lie une variable / constante locale, vous pouvez l'utiliser _pour l'ignorer. Par exemple, si vous souhaitez itérer une séquence sans avoir besoin d'accéder à ses membres:

for _ in 1...20 { // or 0..<20
    // do something 20 times
}

Si vous liez des cas de tuple dans une instruction switch, le trait de soulignement peut fonctionner comme un caractère générique, comme dans cet exemple (raccourci à partir de celui du langage de programmation Swift ):

switch somePoint { // somePoint is an (Int, Int) tuple
case (0, 0):
    print("(0, 0) is at the origin")
case (_, 0):
    print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    print("(0, \(somePoint.1)) is on the y-axis")
default:
    print("(\(somePoint.0), \(somePoint.1)) isn't on an axis")
}

Une dernière chose qui est pas tout à fait comparables , mais que je rajouterais depuis (comme indiqué par les commentaires) il semble amener les gens ici: Un trait de soulignement dans un identifiant - par exemple var _foo, func do_the_thing(),struct Stuff_ - des moyens rien en particulier à Swift, mais a quelques utilisations parmi les programmeurs.

Les traits de soulignement dans un nom sont un choix de style, mais pas préféré dans la communauté Swift, qui a de fortes conventions sur l'utilisation d'UpperCamelCase pour les types et de lowerCamelCase pour tous les autres symboles.

Le préfixage ou le suffixe d'un nom de symbole avec un trait de soulignement est une convention de style, historiquement utilisée pour distinguer les symboles privés / à usage interne uniquement de l'API exportée. Cependant, Swift a des modificateurs d'accès pour cela, donc cette convention est généralement considérée comme non idiomatique dans Swift.

Quelques symboles avec des préfixes à double trait de soulignement ( func __foo()) se cachent dans les profondeurs des SDK d'Apple: Ce sont des symboles (Obj) C importés dans Swift à l'aide de l' NS_REFINED_FOR_SWIFTattribut. Apple l'utilise lorsqu'ils veulent créer une version "plus Swifty" d'une API (Obj) C - par exemple, pour transformer une méthode indépendante du type en méthode générique . Ils doivent utiliser l'API importée pour faire fonctionner la version raffinée de Swift, ils utilisent donc le __pour la maintenir disponible tout en la cachant de la plupart des outils et de la documentation.

Rickster
la source
@rickster, Quelle est la signification du trait de soulignement au début d'une méthode, comme celle-ci-> func _copyContents (initialisation de ptr: UnsafeMutableBufferPointer <Element>) -> (Iterator, UnsafeMutableBufferPointer <Element> .Index)} du protocole Sequence.
dev gr
@devgr: Complètement sans rapport, je vous recommande donc de poster une question distincte.
rickster
Bien sûr, je publierai une question distincte.
dev gr
Merci pour "ne pas attribuer à quoi que ce soit" pour un retour de fonction. C'était ce que je cherchais.
absin le
8

En plus de la réponse acceptée, l'un des cas d'utilisation de _est lorsque vous devez écrire un long nombre dans le code

Numéro plus lisible

Ce n'est pas facile à lire par l'homme:

let x = 1000000000000

Vous pouvez ajouter _le nombre pour le rendre plus lisible par l'homme:

let x = 1_000_000_000_000
Mojtaba Hosseini
la source
6

Depuis Swift 3, la spécification des noms de paramètres lors des appels de fonction est devenue obligatoire - même pour le premier. Donc, comme cela pourrait causer un énorme problème au code écrit dans swift 2, vous pouvez utiliser un trait de soulignement à la déclaration pour éviter d'avoir à écrire le nom du paramètre lors de l'appel. Donc, dans ce cas, il dit "ne vous souciez pas du nom du paramètre externe". Où le nom du paramètre externe est ce que vous appelez les paramètres en dehors de la fonction (à l'appel) et non à l'intérieur. Ces noms de paramètres externes sont appelés étiquettes d'argument. http://ericasadun.com/2016/02/09/the-trouble-with-argument-labels-some-ought/ ... voir comment on donne deux noms au paramètre? Eh bien, le premier est où va le trait de soulignement. J'espère que cela aide, et demandez si vous êtes toujours confus.

Caspar Wylie
la source
4
func divmod(_ a: Int, _ b:Int) -> (Int, Int) {
    return (a / b, a % b)
}

func divmod(a: Int, b:Int) -> (Int, Int) {
    return (a / b, a % b)
}

Le _est un espace réservé pour le nom du paramètre. Dans votre exemple, vous les appelez différemment, dans la deuxième fonction, vous devez écrire le nom du paramètre a: 1.

La convention de nom de fonction de Swift est funcName(param1:param2:), et il a besoin du _comme espace réservé pour créer le nom de la fonction.

Dans le prénom, le nom est

divmod(_:_:)

Alors que le second est

divmod(a:b:)
Davidhu
la source