Czy mogę używać operatora zakresu z instrukcją if w Swift?

197

Czy można użyć operatora zakresu ...i ..<instrukcji if. Maye coś takiego:

let statusCode = 204
if statusCode in 200 ..< 299 {
  NSLog("Success")
}
Jimmy
źródło

Odpowiedzi:

427

Możesz użyć operatora „dopasuj wzór” ~=:

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

Lub instrukcja switch z wzorcem wyrażenia (który wewnętrznie korzysta z operatora dopasowywania wzorców):

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

Zauważ, że ..<oznacza zakres pomijający górną wartość, więc prawdopodobnie chcesz 200 ... 299lub 200 ..< 300.

Informacje dodatkowe: Gdy powyższy kod zostanie skompilowany w Xcode 6.3 z włączonymi optymalizacjami, to do testu

if 200 ... 299 ~= statusCode

właściwie nie jest generowane żadne wywołanie funkcji, tylko trzy instrukcje asemblera:

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

jest to dokładnie ten sam kod zestawu, który jest generowany

if statusCode >= 200 && statusCode <= 299

Możesz to sprawdzić za pomocą

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

Od wersji Swift 2 można to zapisać jako

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

używając nowo wprowadzonego dopasowania wzorca dla instrukcji if. Zobacz także Swift 2 - Dopasowywanie wzorców w „if” .

Martin R.
źródło
1
Fajnie, czy to O (1)? Byłoby również miło, gdyby Swift miał krótką rękę do instrukcji zamiany, takich jak na przykład Scala. Ale biorąc pod uwagę, że zawsze jesteś zmuszony do obsługi wszystkich możliwości w czasie kompilacji w Swift, może to nie być naprawdę wykonalne.
Sky
2
@Sky: Z wygenerowanego kodu asemblera widać, że func ~= (Range<A>, A) -> Boolwywoływana jest funkcja biblioteki . Chciałbym założyć, że funkcja ta działa z O (1).
Martin R
4
@Downvoter: Przydałby się komentarz wyjaśniający, żebym mógł poprawić lub naprawić odpowiedź ...
Martin R
1
@MartinR jak się dowiedzieć, która funkcja jest wywoływana przez język asemblera. +1 za fajną odpowiedź
kodester
3
@codester: Skompilowałem kod w wierszu poleceń xcrun -sdk macosx swift -emit-assembly main.swifti sprawdziłem kod asemblera. Potem zwykłem xcrun swift-demangle ...rozplątywać nazwę wywoływanej funkcji. - Niestety, Xcode nie może jeszcze utworzyć kodu asemblera dla plików Swift, być może zadziała w późniejszej wersji.
Martin R
95

Ta wersja wydaje się bardziej czytelna niż dopasowanie wzorca:

if (200 ... 299).contains(statusCode) {
    print("Success")
}
Serhii Yakovenko
źródło
2
Dokładnie to, czego szukałem
Nazim Kerimbekov
Pojawia się ten błąd => Nie można utworzyć zakresu za pomocą UpperBound <lowerBound
Alfi
9

To stary wątek, ale wydaje mi się, że przesadzamy. Wydaje mi się, że najlepsza odpowiedź jest słuszna

if statusCode >= 200 && statusCode <= 299

Nie ma

if 200 > statusCode > 299

Formularz, który znam, a inne sugerowane rozwiązania wykonują wywołania funkcji, które są trudniejsze do odczytania i mogą być wolniejsze do wykonania. Metoda dopasowania do wzorca jest użyteczną sztuczką, którą należy wiedzieć, ale wydaje się, że nie pasuje do tego problemu.

Edytować:

Osobiście uważam, że operator dopasowania wzorca jest ohydny i chciałbym, żeby kompilator obsługiwał if x in 1...100składnię. To jest o wiele bardziej intuicyjne i łatwe do odczytania niżif 1...100 ~= x

Duncan C.
źródło
1
Masz rację, że ta wersja jest lepsza do czytania, próbowałem tylko odpowiedzieć na wyraźne pytanie „Czy można użyć operatora zasięgu ...?” - Ale Xcode 6.3 beta (w trybie zoptymalizowanym) generuje dokładnie trzy instrukcje montażu dla if 200 ... 299 ~= statusCode, brak wywołania funkcji :)
Martin R.
13
Właściwie if 200 ... 299 ~= statusCodepodaje ten sam kod asemblera coif statusCode >= 200 && statusCode <= 299
Martin R
6
O ile warunek ten nie znajduje się w krytycznej sekcji, która jest odwiedzana tysiące razy na sekundę, martwienie się o narzut wywołania funkcji jest przedwczesną optymalizacją. Nawet wtedy martwię się bardziej o to, co robi wywołanie funkcji , niż o koszt wywołania. Dobra robota @MartinR za udowodnienie, że mimo wszystko nie ma kosztów.
rickster
1
@Rickster, prawda. Nadal preferuję konstrukty wydajne od niewydajnych ze względu na przyzwyczajenie (zakładając, że czytelność jest podobna). Nie do tego stopnia, że ​​tracę na to zbyt wiele MEGO czasu, ale nadal opłaca się wiedzieć, jakie są koszty różnych podejść.
Duncan C
1
To głupota, ale nie zgadzam się z twoją sugestią, że twoje zdanie if jest bardziej czytelne lub zrozumiałe niż odpowiedź wysłana przez @SerhiiYakovenko. Po prostu na podstawie DRY - dwukrotnie nazywasz „statusCode”. W późno-nocnej sesji debugowania z niewyraźnymi oczami, po tym, jak zdecydowałem, że zamiast „statusCode” powinna zostać użyta inna zmienna o nazwie „statusValue”, popełniam błąd, zmieniając jedną z nazw zmiennych, a nie drugą .
RenniePet
3

Chciałem sprawdzić błędy 4xx oprócz 401. Oto kod:

let i = 401

if 400..<500 ~= i, i != 401 {
    print("yes")
} else {
    print("NO")
}
abhimuralidharan
źródło
2

Wolałem też operatora Range .contains (), dopóki nie stwierdziłem, że jego implementacja jest nieefektywna - https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

Możemy przedstawić warunek x <0 za pomocą zakresu: (Int.min .. <0). Contains (x) jest dokładnie równoważne. Jest jednak znacznie wolniejszy. Domyślna implementacja zawiera (_ :) przegląda całą kolekcję, a wykonanie pętli dziewięć kwintillionów w najgorszym przypadku nie jest tanie.

Entro
źródło