Kann ich den Bereichsoperator mit der if-Anweisung in Swift verwenden?

196

Ist es möglich, den Bereichsoperator ...und ..<mit der if-Anweisung zu verwenden ? Maye so etwas:

let statusCode = 204
if statusCode in 200 ..< 299 {
  NSLog("Success")
}
Jimmy
quelle

Antworten:

425

Sie können den Operator "Musterübereinstimmung" verwenden ~=:

if 200 ... 299 ~= statusCode {
    print("success")
}

Oder eine switch-Anweisung mit einem Ausdrucksmuster (das intern den Pattern-Match-Operator verwendet):

switch statusCode {
case 200 ... 299:
    print("success")
default:
    print("failure")
}

Beachten Sie, dass dies ..<einen Bereich bezeichnet, in dem der obere Wert weggelassen wird, sodass Sie wahrscheinlich 200 ... 299oder möchten 200 ..< 300.

Zusätzliche Informationen: Wenn der obige Code in Xcode 6.3 mit aktivierten Optimierungen kompiliert wurde, dann für den Test

if 200 ... 299 ~= statusCode

eigentlich wird überhaupt kein Funktionsaufruf generiert, nur drei Montageanweisungen:

addq    $-200, %rdi
cmpq    $99, %rdi
ja  LBB0_1

Dies ist genau der gleiche Assemblycode, für den generiert wurde

if statusCode >= 200 && statusCode <= 299

Sie können dies mit überprüfen

xcrun -sdk macosx swiftc -O -emit-Assembly main.swift

Ab Swift 2 kann dies wie folgt geschrieben werden

if case 200 ... 299 = statusCode {
    print("success")
}

Verwenden des neu eingeführten Pattern-Matchings für if-Anweisungen. Siehe auch Swift 2 - Pattern Matching in "if" .

Martin R.
quelle
1
Cool, ist das O (1)? Es wäre auch schön, wenn Swift eine kurze Hand für switch-Anweisungen hätte, wie zum Beispiel Scala. Da Sie jedoch immer gezwungen sind, alle Möglichkeiten zur Kompilierungszeit in Swift zu nutzen, ist dies möglicherweise nicht wirklich machbar.
Sky
2
@Sky: Aus dem generierten Assemblycode kann man erkennen, dass eine Bibliotheksfunktion func ~= (Range<A>, A) -> Boolaufgerufen wird. Ich würde annehmen, dass diese Funktion mit O (1) funktioniert.
Martin R
4
@ Downvoter: Einige erklärende Kommentare wären nett, damit ich die Antwort verbessern oder korrigieren kann ...
Martin R
1
@MartinR Wie erfährst du, welche Funktion von der Assemblersprache aufgerufen wird.Hopper? +1 für coole Antwort
Codester
3
@codester: Ich habe den Code in der Befehlszeile mit xcrun -sdk macosx swift -emit-assembly main.swiftdem Assemblycode kompiliert und überprüft. Dann habe ich xcrun swift-demangle ...den Namen der aufgerufenen Funktion entstellt. - Leider kann Xcode noch keinen Assembler-Code für Swift-Dateien erstellen. Möglicherweise funktioniert dies in einer späteren Version.
Martin R
95

Diese Version scheint besser lesbar zu sein als der Mustervergleich:

if (200 ... 299).contains(statusCode) {
    print("Success")
}
Serhii Yakovenko
quelle
2
Genau das, wonach ich gesucht habe
Nazim Kerimbekov
Ich erhalte diesen Fehler => Kann Range nicht mit UpperBound bilden <LowerBound
Alfi
9

Dies ist ein alter Thread, aber es scheint mir, dass wir das überdenken. Mir scheint, die beste Antwort ist gerecht

if statusCode >= 200 && statusCode <= 299

Da ist kein

if 200 > statusCode > 299

Form, die mir bekannt ist, und die anderen vorgeschlagenen Lösungen führen Funktionsaufrufe aus, die schwerer zu lesen und möglicherweise langsamer auszuführen sind. Die Musterübereinstimmungsmethode ist ein nützlicher Trick, scheint jedoch für dieses Problem schlecht geeignet zu sein.

Bearbeiten:

Persönlich finde ich den Pattern-Match-Operator abscheulich und wünschte, der Compiler würde die if x in 1...100Syntax unterstützen. Das ist sooooo viel intuitiver und einfacher zu lesen alsif 1...100 ~= x

Duncan C.
quelle
1
Sie haben Recht, dass diese Version besser zu lesen ist. Ich habe nur versucht, die explizite Frage zu beantworten: "Ist es möglich, den Bereichsoperator zu verwenden ...?" - Aber Xcode 6.3 Beta (im optimierten Modus) generiert genau drei Montageanweisungen für if 200 ... 299 ~= statusCode, kein Funktionsaufruf :)
Martin R
13
if 200 ... 299 ~= statusCodeif statusCode >= 200 && statusCode <= 299
Martin R
6
Sofern sich diese Bedingung nicht in einem kritischen Abschnitt befindet, der tausende Male pro Sekunde aufgerufen wird, ist die Sorge um den Overhead von Funktionsaufrufen eine vorzeitige Optimierung. Selbst dann würde ich mir mehr Gedanken darüber machen, was ein Funktionsaufruf tut, als über die Kosten für den Aufruf. Gute Arbeit @MartinR, um zu beweisen, dass es trotzdem keine Kosten gibt.
Rickster
1
@rickster, wahr genug. Ich bevorzuge aus Gewohnheit immer noch effiziente Konstrukte gegenüber ineffizienten (vorausgesetzt, die Lesbarkeit ist ähnlich). Nicht in dem Maße, in dem ich zu viel Zeit damit verschwende, aber es lohnt sich trotzdem zu wissen, wie hoch die Kosten für verschiedene Ansätze sind.
Duncan C
1
Dies ist kein Problem, aber ich bin nicht einverstanden mit Ihrem Vorschlag, dass Ihre if-Aussage lesbarer oder verständlicher ist als die Antwort von @SerhiiYakovenko. Einfach auf der Basis von DRY - Sie nennen "statusCode" zweimal. In einer nächtlichen Debugging-Sitzung mit trüben Augen, nachdem ich entschieden hatte, dass hier anstelle von "statusCode" eine andere Variable mit dem Namen "statusValue" verwendet werden sollte, könnte ich den Fehler machen, einen der Variablennamen und nicht den anderen zu ändern .
RenniePet
3

Ich wollte 4xx Fehler außer 401 überprüfen. Hier ist der Code:

let i = 401

if 400..<500 ~= i, i != 401 {
    print("yes")
} else {
    print("NO")
}
abhimuralidharan
quelle
2

Ich habe auch den Operator Range .contains () bevorzugt, bis festgestellt wurde, dass seine Implementierung ineffizient ist - https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

Wir können die Bedingung x <0 mit einem Bereich darstellen: (Int.min .. <0) .contains (x) ist genau äquivalent. Es ist jedoch viel langsamer. Die Standardimplementierung von enthält (_ :) durchläuft die gesamte Sammlung, und die Ausführung einer Schleife neun Billionen Mal im schlimmsten Fall ist nicht billig.

Entro
quelle