Swiftのswitchステートメントよりも小さいまたは大きい


145

私はswitchSwiftのステートメントに精通していますが、このコードを次のように置き換える方法を考えていswitchます:

if someVar < 0 {
    // do something
} else if someVar == 0 {
    // do something else
} else if someVar > 0 {
    // etc
}

これは興味深い質問ですが、switchを使用したコードはifステートメントよりもはるかに読みにくいと思います。できるからといって、そうするべきではありません。
Rog

回答:


241

これが1つのアプローチです。someVarIntまたはまたはであると仮定すると、Comparableオプションで新しい変数にオペランドを割り当てることができます。これにより、whereキーワードを使用してスコープを指定できます。

var someVar = 3

switch someVar {
case let x where x < 0:
    print("x is \(x)")
case let x where x == 0:
    print("x is \(x)")
case let x where x > 0:
    print("x is \(x)")
default:
    print("this is impossible")
}

これは少し簡略化できます:

switch someVar {
case _ where someVar < 0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
case _ where someVar > 0:
    print("someVar is \(someVar)")
default:
    print("this is impossible")
}

where範囲一致でキーワードを完全に回避することもできます:

switch someVar {
case Int.min..<0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
default:
    print("someVar is \(someVar)")
}

9
default: fatalError()考えられる論理エラーを早期に検出することをお勧めします。
マーティンR

1
ありがとう!これらの例は非常に役立ち、私の問題を解決します!(他の例も良かったが、あなたの例が私にとって最も役に立ちました)
Pieter

1
@MartinR assertionFailureは、特にチームで作業する場合、より安全なオプションのようです。
Michael Voline

119

Swift 5では、ifステートメントを置き換えるために、次のスイッチのいずれかを選択できます。


#1 PartialRangeFromおよびでスイッチを使用するPartialRangeUpTo

let value = 1

switch value {
case 1...:
    print("greater than zero")
case 0:
    print("zero")
case ..<0:
    print("less than zero")
default:
    fatalError()
}

#2 ClosedRangeおよびでスイッチを使用するRange

let value = 1

switch value {
case 1 ... Int.max:
    print("greater than zero")
case Int.min ..< 0:
    print("less than zero")
case 0:
    print("zero")
default:
    fatalError()
}

#3 where句でスイッチを使用する

let value = 1

switch value {
case let val where val > 0:
    print("\(val) is greater than zero")
case let val where val == 0:
    print("\(val) is zero")
case let val where val < 0:
    print("\(val) is less than zero")
default:
    fatalError()
}

#4 where句でのスイッチの使用と代入 _

let value = 1

switch value {
case _ where value > 0:
    print("greater than zero")
case _ where value == 0:
    print("zero")
case _ where value < 0:
    print("less than zero")
default:
    fatalError()
}

#5 RangeExpressionプロトコルの~=(_:_:)オペレーターでスイッチを使用する

let value = 1

switch true {
case 1... ~= value:
    print("greater than zero")
case ..<0 ~= value:
    print("less than zero")
default:
    print("zero")
}

#6 Equatableプロトコルの~=(_:_:)オペレーターでスイッチを使用する

let value = 1

switch true {
case value > 0:
    print("greater than zero")
case value < 0:
    print("less than zero")
case 0 ~= value:
    print("zero")
default:
    fatalError()
}

#7とスイッチを使用しPartialRangeFromPartialRangeUpToそしてRangeExpressionS」contains(_:)メソッド

let value = 1

switch true {
case (1...).contains(value):
    print("greater than zero")
case (..<0).contains(value):
    print("less than zero")
default:
    print("zero")
}

1
#2でデフォルトのケースが必要なのはなぜですか?範囲がInt.minからInt.maxの場合、何が残っているのでしょうか。
μολὼν.λαβέ

オプションのすごいリスト。これを行う方法はいくつかあります。
Christopher Pickslay

2
概要は良好ですが、0と1の間の数値が考慮されていないため、欠陥があります。0.1ので、致命的なエラーをスローし1...た場合、このソリューションは唯一の1からのカバー数字のみで動作しますのでvalueあるInt変数の型は、任意のコンパイラエラーなしで機能区切りを変更した場合ので、それは危険です。
マヌエル

1
あなたの解決策はダブルタイプでは正しく機能しません。ケース1 ...:print( "ゼロより大きい")0以下であり、1以上である
Vlad

20

switch声明は、ボンネットの下に、使用する~=演算子を。したがって、この:

let x = 2

switch x {
case 1: print(1)
case 2: print(2)
case 3..<5: print(3..<5)
default: break
}

これに砂糖:

if 1          ~= x { print(1) }
else if 2     ~= x { print(2) }
else if 3..<5 ~= x { print(3..<5) }
else {  }

標準ライブラリのリファレンスを見ると、~=がオーバーロードして何をすべきかを正確に知ることができます。含まれているのは範囲マッチングであり、等値化可能なものと同等です。(含まれていないのは、std libの関数ではなく言語機能である列挙型と大文字のマッチングです)

左側のまっすぐなブール値と一致しないことがわかります。これらの種類の比較では、whereステートメントを追加する必要があります。

~=自分でオペレーターをオーバーロードしない限り... (これは一般に推奨されません)1つの可能性は次のようなものです。

func ~= <T> (lhs: T -> Bool, rhs: T) -> Bool {
  return lhs(rhs)
}

つまり、左側のブール値を右側のパラメーターに返す関数と一致します。これは、次のような場合に使用できます。

func isEven(n: Int) -> Bool { return n % 2 == 0 }

switch 2 {
case isEven: print("Even!")
default:     print("Odd!")
}

あなたの場合、次のようなステートメントがあるかもしれません:

switch someVar {
case isNegative: ...
case 0: ...
case isPositive: ...
}

しかし、ここで新しい関数を定義する必要がisNegativeありisPositiveます。さらに演算子をオーバーロードしない限り...

通常の中置演算子をオーバーロードして、カリー化された前置演算子または後置演算子にすることができます。次に例を示します。

postfix operator < {}

postfix func < <T : Comparable>(lhs: T)(_ rhs: T) -> Bool {
  return lhs < rhs
}

これは次のように機能します。

let isGreaterThanFive = 5<

isGreaterThanFive(6) // true
isGreaterThanFive(5) // false

これを以前の関数と組み合わせると、switchステートメントは次のようになります。

switch someVar {
case 0< : print("Bigger than 0")
case 0  : print("0")
default : print("Less than 0")
}

さて、あなたはおそらくこの種のものを実際に使用すべきではありません:それは少し危険です。あなたは(おそらく)そのwhere声明にこだわる方が良いでしょう。とはいえ、switchステートメントパターン

switch x {
case negative:
case 0:
case positive:
}

または

switch x {
case lessThan(someNumber):
case someNumber:
case greaterThan(someNumber):
}

検討する価値があるのに十分一般的なようです。


1
質問に対するあなたの答えはどこですか?見つかりません。
ハニー

1
ケース3 .. <5:print(3 .. <5)-文字通り最初の段落。この答えは過小評価されています。コードをたくさん節約してくれます。
カリム

14

あなたはできる:

switch true {
case someVar < 0:
    print("less than zero")
case someVar == 0:
    print("eq 0")
default:
    print("otherwise")
}

6

誰かがすでに掲載しているので、case let x where x < 0:ここでの場所のための代替です。someVarInt

switch someVar{
case Int.min...0: // do something
case 0: // do something
default: // do something
}

そして、ここでどこのための代替でsomeVarあるがDouble

case -(Double.infinity)...0: // do something
// etc

6

これは、範囲でどのように見えるかです

switch average {
case 0..<40: //greater or equal than 0 and less than 40
    return "T"
case 40..<55: //greater or equal than 40 and less than 55
    return "D"
case 55..<70: //greater or equal than 55 and less than 70
    return "P"
case 70..<80: //greater or equal than 70 and less than 80
    return "A"
case 80..<90: //greater or equal than 80 and less than 90
    return "E"
case 90...100: //greater or equal than 90 and less or equal than 100
    return "O"
default:
    return "Z"
}

3

<0式は(もう?)は動作しません、私はこれで終わったので:

Swift 3.0:

switch someVar {
    case 0:
        // it's zero
    case 0 ..< .greatestFiniteMagnitude:
        // it's greater than zero
    default:
        // it's less than zero
    }

1
swift 3.0ではX_MAX.greatestFiniteMagnitudeDouble.greatestFiniteMagnitudeCGFloat.greatestFiniteMagnitudecase 0..< .greatestFiniteMagnitudesomeVar
、、

@Dorian Roy オペレーターが認識されないのvar timeLeft = 100 switch timeLeft {case 0...<=7200: print("ok") default:print("nothing") }はなぜ <=ですか?イコールなしで書いた場合、動作します。おかげで
bibscy

@bibscy閉じた範囲の演算子を使用します。case 0...7200:演算子<=は比較演算子です。スイッチでは、範囲演算子のみを使用できます(ドキュメントを参照)
Dorian Roy

これは素晴らしかった。私はこのエラーになった型'int型の値と一致することはできません「レンジ<ダブル>」タイプの発現パターンを、私はので、someVarだったIntと私がしなければならなかったDouble(... `それを動作させるために)SOMEVARを
ハニー

2

Swift 4が問題に対処できてうれしい:

3での回避策として、次のことを行いました。

switch translation.x  {
case  0..<200:
    print(translation.x, slideLimit)
case  -200..<0:
    print(translation.x, slideLimit)
default:
    break
}

機能するが理想的ではない

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