Swiftのifステートメントで範囲演算子を使用できますか?


196

範囲演算子.....<ifステートメントを使用することは可能ですか?このようなメア:

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

回答:


425

「パターン一致」演算子を使用できます~=

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

実際には関数呼び出しはまったく生成されず、3つのアセンブリ命令のみが生成されます。

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ステートメントに新しく導入されたパターンマッチングを使用します。「if」のSwift 2-パターンマッチングも参照してください。


1
かしこまりました、これはO(1)ですか?また、Swiftのように、たとえばScalaのように、switchステートメントの省略形を用意しておくとよいでしょう。しかし、Swiftでのコンパイル時に常にすべての可能性を処理することを余儀なくされていることを考えると、それは実際に実現可能ではない可能性があります。
スカイ14

2
@Sky:生成されたアセンブリコードから、ライブラリ関数func ~= (Range<A>, A) -> Boolが呼び出されていることがわかります。この関数はO(1)で機能すると仮定します。
マーティンR

4
@Downvoter:いくつかの説明コメントがいいので、答えを改善または修正できます...
Martin R

1
@MartinRアセンブリ言語によって呼び出された関数をどのようにして知ることができますか?クールな回答の+1
コードスター2014

3
@codester:コマンドラインでコードをコンパイルしxcrun -sdk macosx swift -emit-assembly main.swift、アセンブリコードを検査しました。次にxcrun swift-demangle ...、呼び出された関数の名前をデマングルしました。-残念ながら、XcodeはまだSwiftファイルのアセンブリコードを作成できません。おそらくそれ以降のバージョンで動作するでしょう。
マーティンR 14

95

このバージョンは、パターンマッチングよりも読みやすいようです。

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

2
まさに私が探していたもの
Nazim Kerimbekov

私が手にこのエラー=>は、UpperBound <下界でレンジを形成することはできません
ALFI

9

これは古いスレッドですが、考えすぎているようです。私には最良の答えは

if statusCode >= 200 && statusCode <= 299

ありません

if 200 > statusCode > 299

私が知っているフォーム、および他の提案されたソリューションは、関数呼び出しを行っています。これは読みにくく、実行が遅くなる可能性があります。パターンマッチの方法は知っておくと便利ですが、この問題にはあまり適していません。

編集:

個人的には、パターンマッチ演算子は恐ろしいと思うので、コンパイラーがif x in 1...100構文をサポートすることを願っています。それは非常に直感的で読みやすいですif 1...100 ~= x


1
このバージョンの方が読みやすいというのはあなたの言うとおりです。「範囲演算子を使用することは可能ですか?」という明確な質問に答えようとしただけです。しかし、 Xcode 6.3ベータ(最適化モード)は、3つのアセンブリ命令を生成します。if 200 ... 299 ~= statusCode関数呼び出しはありません:)
Martin R

13
実際if 200 ... 299 ~= statusCode同じアセンブリコードを提供しますif statusCode >= 200 && statusCode <= 299
Martin R

6
この条件が1秒あたり数千回アクセスされるクリティカルセクションにない限り、関数呼び出しのオーバーヘッドを心配することは時期尚早の最適化です。それでも、私は、関数呼び出しが何であるかについてもっと心配と思いやってそれを呼び出すのではなく、コストを。とはいえ、費用がかからないことを証明してくれた@MartinRは素晴らしい仕事です。
リクスター、2015

1
@rickster、真実。私はまだ習慣の問題として、非効率なものよりも効率的な構成を好む傾向があります(読みやすさが同じであると仮定)。私の時間を無駄にしすぎているわけではありませんが、さまざまなアプローチのコストを把握することは価値があります。
Duncan C

1
これはつまらないですが、あなたのifステートメントが@SerhiiYakovenkoによって投稿された回答よりも読みやすく、理解しやすいというあなたの提案には同意しません。単にDRYに基づいて-「statusCode」に2回名前を付けます。ここで「statusCode」の代わりに「statusValue」という名前の異なる変数を使用することを決定した後の深夜の目の疲れたデバッグセッションでは、変数名の1つを変更し、もう1つは変更しないという間違いを犯す可能性があります。 。
RenniePet 2016

3

401以外の4xxエラーを確認したいと思います。コードは次のとおりです。

let i = 401

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

2

その実装は非効率的であることが判明するまで、私は、あまりにも範囲.contains()演算子を優先- https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

範囲を使用して、条件x <0を表すことができます:(Int.min .. <0).contains(x)はまったく同じです。しかし、それは非常に遅いです。contains(_ :)のデフォルトの実装は、コレクション全体を走査します。最悪の場合、ループを9千億回実行するのは安くはありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.