Чи можу я використовувати оператор діапазону, якщо оператор if у Swift?


198

Чи можливо використовувати оператор діапазону ...і ..<з оператором if. Може щось подібне:

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

Відповіді:


428

Ви можете використовувати оператор "відповідність шаблону" ~=:

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
Класно, це О (1)? Крім того, було б непогано, якби у Свіфта була коротка рука для тверджень про перемикання, як, наприклад, Скала. Але враховуючи, що ви завжди змушені обробляти всі можливості під час компіляції в Swift, це може бути не реально.
Небо

2
@Sky: З створеного коду збірки видно, що викликається функція бібліотеки func ~= (Range<A>, A) -> Bool. Я б припустив, що ця функція працює з O (1).
Мартін Р

4
@Downvoter: Деякі пояснювальні коментарі були б непоганими, щоб я міг покращити або виправити відповідь ...
Martin R

1
@MartinR як ти дізнаєшся, яку функцію викликає мова збірки.Hopper? +1 за класну відповідь
кодестер

3
@codester: Я склав код у командному рядку xcrun -sdk macosx swift -emit-assembly main.swiftі перевірив код складання. Тоді я використовував xcrun swift-demangle ...для вимикання назви виклику функції. - На жаль, Xcode ще не може створити код складання для файлів Swift, можливо, він буде працювати в більш пізній версії.
Martin R

95

Ця версія здається більш читаною, ніж відповідність шаблонів:

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

2
Саме те, що я шукав
Назим Керимбеков

Я отримую цю помилку => Не можу сформувати діапазон з верхньою ланкою <нижняBound
Алфі

10

Це стара нитка, але мені здається, ми це надмірно думаємо. Мені здається, найкраща відповідь - це просто

if statusCode >= 200 && statusCode <= 299

Немає

if 200 > statusCode > 299

Форма, про яку мені відомо, та інші запропоновані рішення виконують виклики функцій, які важче читати, а виконання може бути повільніше. Метод відповідності шаблонів є корисним трюком, який слід знати, але здається поганим підходом до цієї проблеми.

Редагувати:

Особисто я вважаю, що оператор відповідності шаблонів є огидним і хочу, щоб компілятор підтримував if x in 1...100синтаксис. Це sooooo набагато інтуїтивніше і легше для читання, ніж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, досить правда. Я як і раніше віддаю перевагу ефективним конструкціям над неефективними за звичкою (припускаючи, що читабельність схожа). Не в тій мірі, на яку я витрачаю занадто багато мого часу на це, але все одно варто платити, щоб знати, які витрати на різні підходи.
Duncan C

1
Це викликає недоторканність, але я не погоджуюся з вашою пропозицією про те, що ваше твердження if, якщо воно читається чи зрозуміліше, ніж відповідь, опублікована @SerhiiYakovenko. Просто на основі DRY - ви двічі назиєте "statusCode". У сеансі налагодження пізно-нічних очей після того, як я вирішив, що іншу змінну під назвою "statusValue" слід використовувати тут замість "statusCode", я просто можу помилитися, змінивши одну з назв змінної, а не іншу .
RenniePet


2

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.