Ít hơn hoặc lớn hơn trong câu lệnh chuyển đổi Swift


145

Tôi quen thuộc với các switchcâu lệnh trong Swift, nhưng tự hỏi làm thế nào để thay thế đoạn mã này bằng switch:

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

Mặc dù đây là một câu hỏi thú vị, tôi nghĩ rằng mã sử dụng switch ít dễ đọc hơn các câu lệnh if. Chỉ vì bạn có thể, không có nghĩa là bạn nên.
Rog

Câu trả lời:


241

Đây là một cách tiếp cận. Giả sử someVarlà một Inthoặc khác Comparable, bạn có thể tùy ý gán toán hạng cho một biến mới. Điều này cho phép bạn phạm vi nó theo cách bạn muốn bằng cách sử dụng wheretừ khóa:

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")
}

Điều này có thể được đơn giản hóa một chút:

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")
}

Bạn cũng có thể tránh wherehoàn toàn từ khóa với phạm vi phù hợp:

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

9
Tôi khuyên bạn nên default: fatalError()phát hiện sớm các lỗi logic có thể xảy ra.
Martin R

1
Cảm ơn! Những ví dụ này rất hữu ích và chúng giải quyết vấn đề của tôi! (các ví dụ khác cũng tốt, nhưng bạn rất hữu ích với tôi)
Pieter

1
@MartinR assertionFailuredường như là một lựa chọn an toàn hơn, đặc biệt là khi làm việc trong nhóm.
Michael Voline

119

Với Swift 5, bạn có thể chọn một trong các công tắc sau để thay thế câu lệnh if của mình.


# 1 Sử dụng chuyển đổi với PartialRangeFromPartialRangeUpTo

let value = 1

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

# 2 Sử dụng chuyển đổi với ClosedRangeRange

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 Sử dụng chuyển đổi với mệnh đề 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 Sử dụng chuyển đổi với mệnh đề where và gán cho _

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 Sử dụng chuyển đổi với toán tử RangeExpressiongiao thức~=(_:_:)

let value = 1

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

# 6 Sử dụng chuyển đổi với toán tử Equatablegiao thức~=(_:_:)

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 Sử dụng công tắc với PartialRangeFrom, PartialRangeUpToRangeExpression's contains(_:)phương pháp

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
Tại sao trường hợp mặc định cần thiết trong # 2? Có vẻ lúng túng rằng nếu cuộc trả thù là từ Int.min đến Int.max thì còn lại gì?
μολὼν.λαβέ

Wow, danh sách tốt đẹp của các tùy chọn. Tốt để biết có một số cách để làm điều này.
Christopher Pickslay

2
Tổng quan tốt nhưng thiếu sót vì các số từ 0 đến 1 không được tính cho. 0.1ném một lỗi nghiêm trọng vì 1...chỉ bao gồm các số từ 1. Vì vậy, giải pháp này chỉ hoạt động nếu valuelà một Intnhưng điều đó là nguy hiểm vì nếu loại biến thay đổi chức năng bị phá vỡ mà không có lỗi biên dịch.
Manuel

1
Giải pháp của bạn không hoạt động chính xác cho loại Double. trường hợp 1 ...: print ("lớn hơn 0") KHÔNG lớn hơn 0, lớn hơn hoặc bằng 1.
Vlad

20

Câu switchlệnh, dưới mui xe, sử dụng ~=toán tử. Vậy đây:

let x = 2

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

Desugars cho điều này:

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

Nếu bạn nhìn vào tài liệu tham khảo thư viện tiêu chuẩn, nó có thể cho bạn biết chính xác những gì ~=bị quá tải để làm : bao gồm là khớp phạm vi và đánh đồng cho những thứ tương đương. (Không bao gồm khớp enum-case, là một tính năng ngôn ngữ, chứ không phải là một chức năng trong lib std)

Bạn sẽ thấy rằng nó không khớp với một boolean thẳng ở phía bên trái. Đối với những loại so sánh đó, bạn cần thêm một câu lệnh where.

Trừ khi ... bạn tự làm quá tải người ~=vận hành. (Điều này thường không được khuyến nghị) Một khả năng sẽ là như thế này:

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

Vì vậy, nó phù hợp với một hàm trả về một boolean ở bên trái với tham số của nó ở bên phải. Đây là loại điều bạn có thể sử dụng nó cho:

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

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

Đối với trường hợp của bạn, bạn có thể có một tuyên bố như thế này:

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

Nhưng bây giờ bạn phải xác định mới isNegativeisPositive chức năng. Trừ khi bạn quá tải một số toán tử ...

Bạn có thể quá tải các toán tử infix bình thường để trở thành các toán tử tiền tố hoặc hậu tố. Đây là một ví dụ:

postfix operator < {}

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

Điều này sẽ làm việc như thế này:

let isGreaterThanFive = 5<

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

Kết hợp điều đó với chức năng trước đó và câu lệnh chuyển đổi của bạn có thể trông như thế này:

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

Bây giờ, có lẽ bạn không nên sử dụng loại điều này trong thực tế: đó là một chút tinh ranh. Bạn (có lẽ) tốt hơn hết là gắn bó với wheretuyên bố. Điều đó nói rằng, mẫu câu lệnh chuyển đổi của

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

hoặc là

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

Có vẻ đủ phổ biến cho nó là đáng để xem xét.


1
câu trả lời của bạn ở đâu? Tôi không thể tìm thấy nó.
Mật ong

1
trường hợp 3 .. <5: in (3 .. <5) - Nghĩa đen trong đoạn đầu tiên. Câu trả lời này được đánh giá thấp. Tiết kiệm cho tôi rất nhiều mã.
Karim

14

Bạn có thể:

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

6

Vì ai đó đã đăng case let x where x < 0:ở đây là một thay thế cho nơi someVarlà một Int.

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

Và đây là một thay thế cho someVarmột Double:

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

6

Đây là cách nó trông giống như với phạm vi

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

Các <0biểu hiện không làm việc (nữa?) Vì vậy tôi đã kết thúc với điều này:

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
Trong swift 3.0, X_MAXđã được thay thế bởi .greatestFiniteMagnitude, tức là Double.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitudev.v. Thông thường, bạn chỉ có thể thực hiện case 0..< .greatestFiniteMagnitudevì loại someVarđã được biết đến
Guig

@Dorian Roy var timeLeft = 100 switch timeLeft {case 0...<=7200: print("ok") default:print("nothing") }Tại sao <=nhà điều hành không được công nhận? Nếu tôi viết nó mà không bằng nó hoạt động. Cảm ơn
bibscy

@bibscy Bạn muốn sử dụng toán tử phạm vi đóng: case 0...7200:Toán tử <=là toán tử so sánh. Trong một chuyển đổi, bạn chỉ có thể sử dụng các toán tử phạm vi (xem tài liệu)
Dorian Roy

Điều này thật tuyệt Tôi đã nhận được mẫu biểu thức lỗi này thuộc loại 'Phạm vi <Double>' không thể khớp với các giá trị của loại 'Int' vì tôi someVarlà một Intvà tôi phải thực hiện một Double(sốVar) `để làm cho nó hoạt động ...
Honey

2

Rất vui khi Swift 4 giải quyết vấn đề:

Như một cách giải quyết trong 3 tôi đã làm:

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

Hoạt động nhưng không lý tưởng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.