Могу ли я использовать оператор диапазона с оператором if в Swift?

198

Можно ли использовать оператор диапазона ...и ..<с оператором if. Может что-то вроде этого:

let statusCode = 204
if statusCode in 200 ..< 299 {
  NSLog("Success")
}
Джимми
источник

Ответы:

428

Вы можете использовать оператор "pattern-match" ~=:

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

Или оператор switch с шаблоном выражения (который внутренне использует оператор сопоставления с шаблоном):

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

Обратите внимание, что ..<обозначает диапазон, в котором отсутствует верхнее значение, так что вы, вероятно, хотите 200 ... 299или 200 ..< 300.

Дополнительная информация: Когда приведенный выше код скомпилирован в Xcode 6.3 с включенной оптимизацией, то для теста

if 200 ... 299 ~= statusCode

фактически никакого вызова функции не генерируется, только три инструкции по сборке:

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

это точно такой же код сборки, который генерируется для

if statusCode >= 200 && statusCode <= 299

Вы можете проверить это с

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

Начиная со Swift 2 это можно записать как

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

использование недавно введенного сопоставления с образцом для операторов if. См. Также Swift 2 - сопоставление с образцом в «если» .

Мартин Р
источник
1
Круто, это O (1)? Также было бы неплохо, если бы у Swift была короткая рука для операторов switch, как, например, Scala. Но, учитывая, что вы всегда вынуждены обрабатывать все возможности во время компиляции в Swift, это может быть нереально.
Небо
2
@Sky: Из сгенерированного ассемблерного кода видно, что func ~= (Range<A>, A) -> Boolвызывается библиотечная функция . Я бы предположил, что эта функция работает с O (1).
Мартин Р
4
@Downvoter: Некоторые поясняющие комментарии были бы хорошими, так что я могу улучшить или исправить ответ ...
Martin R
1
@MartinR, как узнать, какая функция вызывается языком ассемблера? +1 за крутой ответ
кодер
3
@codester: я скомпилировал код в командной строке xcrun -sdk macosx swift -emit-assembly main.swiftи проверил код сборки. Затем я использовал xcrun swift-demangle ...для удаления названия вызываемой функции. - К сожалению, Xcode пока не может создать ассемблерный код для файлов Swift, возможно, он будет работать в более поздней версии.
Мартин Р
95

Эта версия кажется более читаемой, чем сопоставление с образцом:

if (200 ... 299).contains(statusCode) {
    print("Success")
}
Сергей Яковенко
источник
2
Именно то, что я искал
Назим Керимбеков
Я получаю эту ошибку => Не удается сформировать диапазон с UpperBound <LowerBound
Альфи
10

Это старая ветка, но мне кажется, мы обдумываем это. Мне кажется, лучший ответ - просто

if statusCode >= 200 && statusCode <= 299

Нет никаких

if 200 > statusCode > 299

форма, о которой я знаю, и другие предлагаемые решения выполняют вызовы функций, которые труднее читать и могут выполнять медленнее. Метод сопоставления с образцом - полезная уловка, но он плохо подходит для этой проблемы.

Редактировать:

Лично я считаю оператор сопоставления с образцом отвратительным и хотел бы, чтобы компилятор поддерживал if x in 1...100синтаксис. Это оооочень более интуитивно и легко читается, чемif 1...100 ~= x

Дункан С
источник
1
Вы правы, что эту версию лучше читать, я просто попытался ответить на явный вопрос "Можно ли использовать оператор диапазона ...?" - Но бета-версия Xcode 6.3 (в оптимизированном режиме) генерирует ровно три инструкции по сборке для if 200 ... 299 ~= statusCode, без вызова функции :)
Martin R
13
На самом деле if 200 ... 299 ~= statusCodeдает тот же код сборки, что иif statusCode >= 200 && statusCode <= 299
Martin R
6
Если это условие не находится в критической секции, которую посещают тысячи раз в секунду, беспокойство по поводу издержек вызова функции является преждевременной оптимизацией. Даже тогда я бы больше беспокоился о том, что делает вызов функции , а не о стоимости ее вызова. Хорошая работа @MartinR для доказательства того, что нет никаких затрат, хотя.
Рикстер
1
@rickster, достаточно верно. Я по-прежнему склонен предпочитать эффективные конструкции неэффективным по привычке (при условии, что читаемость похожа). Не в той степени, что я трачу на это слишком много МОЕГО времени, но все равно стоит знать, какова стоимость различных подходов.
Дункан C
1
Это придирчиво, но я не согласен с вашим предположением о том, что ваше утверждение if более читабельно или понятно, чем ответ, опубликованный @SerhiiYakovenko. Просто на основе СУХОГО - вы дважды называете «statusCode». В сеансе отладки поздно вечером, когда я решил, что здесь должна использоваться другая переменная с именем «statusValue» вместо «statusCode», я просто могу ошибиться, изменив одно из имен переменных, а не другое ,
RenniePet
3

Я хотел проверить ошибки 4xx, кроме 401. Вот код:

let i = 401

if 400..<500 ~= i, i != 401 {
    print("yes")
} else {
    print("NO")
}
abhimuralidharan
источник
2

Я тоже предпочитал оператор Range .contains (), пока не обнаружил, что его реализация неэффективна - https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

Мы можем представить условие x <0, используя диапазон: (Int.min .. <0) .contains (x) в точности эквивалентно. Это значительно медленнее, хотя. Реализация по умолчанию метода содержит (_ :) обходит всю коллекцию, и выполнение цикла девять раз по пять раз в худшем случае недешево.

Entro
источник