Posso usare l'operatore di intervallo con l'istruzione if in Swift?


198

È possibile utilizzare l'operatore di intervallo ...e ..<con l'istruzione if. Maye qualcosa del genere:

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

Risposte:


428

È possibile utilizzare l'operatore "pattern-match" ~=:

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

O un'istruzione switch con un pattern di espressione (che utilizza internamente l'operatore pattern-match):

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

Si noti che ..<indica un intervallo che omette il valore superiore, quindi probabilmente si desidera 200 ... 299o 200 ..< 300.

Ulteriori informazioni: quando il codice sopra è compilato in Xcode 6.3 con le ottimizzazioni attivate, quindi per il test

if 200 ... 299 ~= statusCode

in realtà non viene generata alcuna chiamata di funzione, solo tre istruzioni di assemblaggio:

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

questo è esattamente lo stesso codice assembly che viene generato per

if statusCode >= 200 && statusCode <= 299

Puoi verificarlo con

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

A partire da Swift 2, questo può essere scritto come

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

usando il pattern-matching appena introdotto per le istruzioni if. Vedi anche Swift 2 - Pattern matching in "if" .


1
Bene, questo è O (1)? Inoltre, sarebbe bello se Swift avesse una scorciatoia per le istruzioni switch, come ad esempio Scala. Ma dato che sei sempre costretto a gestire tutte le possibilità in fase di compilazione in Swift, potrebbe non essere realmente fattibile.
Sky,

2
@Sky: dal codice assembly generato si può vedere che func ~= (Range<A>, A) -> Boolviene chiamata una funzione di libreria . Vorrei pensare che questa funzione funziona con O (1).
Martin R,

4
@Downvoter: Alcuni commenti esplicativi sarebbero graditi, in modo che io possa migliorare o correggere la risposta ...
Martin R

1
@MartinR come si fa a sapere quale funzione viene chiamata dal linguaggio assembly. +1 per una bella risposta
codester

3
@codester: ho compilato il codice sulla riga di comando xcrun -sdk macosx swift -emit-assembly main.swifte ho ispezionato il codice assembly. Quindi ho usato xcrun swift-demangle ...per decodificare il nome della funzione chiamata. - Sfortunatamente, Xcode non può ancora creare il codice assembly per i file Swift, forse funzionerà in una versione successiva.
Martin R,

95

Questa versione sembra essere più leggibile della corrispondenza dei modelli:

if (200 ... 299).contains(statusCode) {
    print("Success")
}

2
Esattamente quello che stavo cercando
Nazim Kerimbekov,

Ottengo questo errore => Impossibile formare l'intervallo con upperBound <lowerBound
Alfi

10

Questo è un vecchio thread, ma mi sembra che ci stiamo pensando troppo. Mi sembra che la risposta migliore sia giusta

if statusCode >= 200 && statusCode <= 299

Non c'è

if 200 > statusCode > 299

di cui sono a conoscenza e le altre soluzioni suggerite stanno effettuando chiamate di funzione, che sono più difficili da leggere e potrebbero essere più lente da eseguire. Il metodo di corrispondenza del modello è un trucco utile da sapere, ma sembra inadatto a questo problema.

Modificare:

Personalmente, trovo che l'operatore di pattern match sia orribile e vorrei che il compilatore supportasse la if x in 1...100sintassi. Questo è molto più intuitivo e facile da leggere diif 1...100 ~= x


1
Hai ragione a leggere questa versione, ho appena provato a rispondere alla domanda esplicita "È possibile utilizzare l'operatore di intervallo ...?" - Ma Xcode 6.3 beta (in modalità ottimizzata) genera esattamente tre istruzioni di assemblaggio per if 200 ... 299 ~= statusCode, nessuna chiamata di funzione :)
Martin R

13
In realtà if 200 ... 299 ~= statusCodefornisce lo stesso codice assembly diif statusCode >= 200 && statusCode <= 299
Martin R

6
A meno che questo condizionale non si trovi in ​​una sezione critica che viene visitata migliaia di volte al secondo, preoccuparsi dell'overhead delle chiamate di funzione è l'ottimizzazione prematura. Anche allora, mi preoccuperei di più di ciò che sta facendo una chiamata di funzione piuttosto che del costo di chiamarla. Bel lavoro @MartinR per dimostrare che non ci sono costi, comunque.
rickster

1
@Rickster, abbastanza vero. Tendo ancora a preferire costrutti efficienti rispetto a quelli inefficienti come una questione di abitudine (supponendo che la leggibilità sia simile). Non nella misura in cui spreco troppo del MIO tempo su di esso, ma è comunque utile sapere quali sono i costi dei diversi approcci.
Duncan C

1
Questo è pignolo, ma non sono d'accordo con il tuo suggerimento che la tua dichiarazione if sia più leggibile o comprensibile della risposta pubblicata da @SerhiiYakovenko. Semplicemente sulla base di DRY - si chiama "statusCode" due volte. In una sessione di debug con gli occhi annebbiati a tarda notte dopo aver deciso che una diversa variabile denominata "statusValue" dovrebbe essere utilizzata qui invece di "statusCode", potrei solo fare l'errore di cambiare uno dei nomi delle variabili e non l'altro .
RenniePet,

3

Volevo controllare gli errori 4xx tranne 401. Ecco il codice:

let i = 401

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

2

Ho preferito anche l'operatore Range .contains (), fino a quando non ho scoperto che la sua implementazione è inefficiente - https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

Possiamo rappresentare la condizione x <0 usando un intervallo: (Int.min .. <0) .contains (x) è esattamente equivalente. Tuttavia è molto più lento. L'implementazione predefinita di contiene (_ :) attraversa l'intera raccolta e l'esecuzione di un ciclo di nove quintilioni di volte nel peggiore dei casi non è economica.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.