Dlaczego szybko potrzebuję podkreślenia?

161

Tutaj jest napisane: „Uwaga: _oznacza„ Nie obchodzi mnie ta wartość ””, ale pochodząc z JavaScript, nie rozumiem, co to znaczy.

Jedynym sposobem na wydrukowanie tych funkcji było użycie podkreślenia przed parametrami:

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

Bez podkreśleń muszę napisać to tak, żeby uniknąć błędów:

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

Nie rozumiem tego użycia podkreślenia. Kiedy, jak i dlaczego używam tych podkreśleń?

Michael Rader
źródło

Odpowiedzi:

353

Istnieje kilka niuansów dotyczących różnych przypadków użycia, ale ogólnie podkreślenie oznacza „zignoruj ​​to”.


Podczas deklarowania nowej funkcji podkreślenie mówi Swiftowi, że parametr nie powinien mieć etykiety po wywołaniu - tak właśnie jest. Pełniejsza deklaracja funkcji wygląda następująco:

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

„etykieta” jest etykietą argumentu i musi być obecna podczas wywoływania funkcji. (Od wersji Swift 3, etykiety są domyślnie wymagane dla wszystkich argumentów). „Nazwa” to nazwa zmiennej dla tego argumentu, której używasz wewnątrz funkcji. Krótsza forma wygląda następująco:

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

Jest to skrót, który umożliwia użycie tego samego słowa zarówno dla etykiety argumentu zewnętrznego, jak i nazwy parametru wewnętrznego. To odpowiednik func myFunc(name name: Int).

Jeśli chcesz, aby funkcja była wywoływalna bez etykiet parametrów, użyj podkreślenia, _aby etykieta była pusta / ignorowana. (W takim przypadku musisz podać nazwę wewnętrzną, jeśli chcesz mieć możliwość korzystania z parametru).

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

W instrukcji przypisania podkreślenie oznacza „nie przypisuj niczego”. Możesz tego użyć, jeśli chcesz wywołać funkcję, która zwraca wynik, ale nie przejmuje się zwracaną wartością.

_ = someFunction()

Lub, jak w artykule, do którego utworzyłeś link, aby zignorować jeden element zwróconej krotki:

let (x, _) = someFunctionThatReturnsXandY()

Kiedy piszesz zamknięcie, które implementuje określony typ funkcji, możesz użyć podkreślenia, aby zignorować określone parametry.

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

Podobnie podczas deklarowania funkcji, która przyjmuje protokół lub przesłania metodę nadklasy, można użyć nazw_ parametrów, aby zignorować parametry. Ponieważ protokół / nadklasa może również określać, że parametr nie ma etykiety, możesz nawet skończyć z dwoma podkreśleniami z rzędu.

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

Nieco związane z dwoma ostatnimi stylami: kiedy używasz konstrukcji sterującej przepływem, która wiąże lokalną zmienną / stałą, możesz _ją zignorować. Na przykład, jeśli chcesz iterować sekwencję bez konieczności dostępu do jej elementów członkowskich:

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

Jeśli wiążesz przypadki krotek w instrukcji switch, podkreślenie może działać jako symbol wieloznaczny, jak w tym przykładzie (skróconym z jednego w języku programowania 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")
}

Ostatnią rzeczą, która nie dość podobne, lecz ja to ponieważ (jak zauważył komentarzach) wydaje się prowadzić ludzi tutaj: podkreślenia w identyfikator - np var _foo, func do_the_thing(), struct Stuff_- nic nie znaczy w szczególności Swift, ale ma kilka zastosowań wśród programistów.

Podkreślenia w nazwie są wyborem stylu, ale nie są preferowane w społeczności Swift, która ma silne konwencje dotyczące używania UpperCamelCase dla typów i lowerCamelCase dla wszystkich innych symboli.

Przedrostek lub sufiks nazwy symbolu podkreśleniem jest konwencją stylów, która w przeszłości służyła do odróżniania symboli prywatnych / przeznaczonych tylko do użytku wewnętrznego od eksportowanego interfejsu API. Jednak Swift ma do tego modyfikatory dostępu, więc ta konwencja jest ogólnie postrzegana jako nieidiomatyczna w Swift.

Kilka symboli z przedrostkami z podwójnym podkreśleniem ( func __foo()) czai się w głębi SDK firmy Apple: Są to symbole (Obj) C zaimportowane do Swift przy użyciu NS_REFINED_FOR_SWIFTatrybutu. Apple używa tego, gdy chce stworzyć „bardziej Swifty” wersję interfejsu API (Obj) C - na przykład, aby przekształcić metodę niezależną od typu w metodę ogólną . Muszą używać zaimportowanego interfejsu API, aby udoskonalona wersja Swift działała, więc używają go, __aby był dostępny, ukrywając go przed większością narzędzi i dokumentacji.

rickster
źródło
@rickster, Jakie jest znaczenie podkreślenia na początku metody, takiej jak ta one-> func _copyContents (inicjalizacja ptr: UnsafeMutableBufferPointer <Element>) -> (Iterator, UnsafeMutableBufferPointer <Element> .Index)} z protokołu Sequence.
dev gr
@devgr: Zupełnie niezwiązane, więc radzę zadać osobne pytanie.
rickster
Jasne, zadam osobne pytanie.
dev gr
Dziękuję za „nie przypisuj niczego” w celu zwrócenia funkcji. To było to, czego szukałem.
absin
8

Oprócz zaakceptowanej odpowiedzi, jednym z przypadków użycia _jest wpisanie długiej liczby w kodzie

Bardziej czytelny numer

To nie jest łatwe do odczytania przez człowieka:

let x = 1000000000000

Możesz dodać _numer, aby był bardziej czytelny dla człowieka:

let x = 1_000_000_000_000
Mojtaba Hosseini
źródło
6

Od wersji Swift 3 określanie nazw parametrów w wywołaniach funkcji stało się obowiązkowe - nawet w przypadku pierwszego. Tak więc, ponieważ może to spowodować ogromny problem w kodzie napisanym w języku swift 2, możesz użyć podkreślenia w deklaracji, aby uniknąć konieczności wpisywania nazwy parametru podczas wywołania. W tym przypadku jest to informacja „nie przejmuj się nazwą parametru zewnętrznego”. Gdzie nazwa parametru zewnętrznego jest tym, co nazywasz parametrami poza funkcją (na żądanie), a nie wewnątrz. Te nazwy parametrów zewnętrznych nazywane są etykietami argumentów. http://ericasadun.com/2016/02/09/the-trouble-with-argument-labels-some-hardts/ ... zobacz, jak parametrowi nadano dwie nazwy? cóż, pierwszy dotyczy podkreślenia. Mam nadzieję, że to pomoże i zapytaj, czy nadal nie masz pojęcia.

Caspar Wylie
źródło
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)
}

_Jest symbolem zastępczym dla nazwy parametru. W twoim przykładzie nazywasz je inaczej, w drugiej funkcji musisz wpisać nazwę parametru a: 1.

Konwencja nazw funkcji w Swift jest taka funcName(param1:param2:)i potrzebuje _symbolu zastępczego do utworzenia nazwy funkcji.

W pierwszym imieniu brzmi

divmod(_:_:)

Podczas gdy drugi jest

divmod(a:b:)
davidhu
źródło