Standard way to “clamp” a number between two values in Swift

后端 未结 8 1251
梦谈多话
梦谈多话 2020-12-02 15:10

Given:

let a = 4.2
let b = -1.3
let c = 6.4

I want to know the simplest, Swiftiest way to clamp these values to a given range, say 0...

8条回答
  •  时光取名叫无心
    2020-12-02 15:46

    The ClosedInterval type already has a

    func clamp(_ intervalToClamp: ClosedInterval) -> ClosedInterval
    

    method which takes another interval as an argument. There is a proposal on the Swift evolution mailing list

    • Add clamp(value: Bound) -> Bound to ClosedInterval

    to add another method which clamps a single value to the given interval:

    /// Returns `value` clamped to `self`.
    func clamp(value: Bound) -> Bound
    

    and that is exactly what you need.

    Using the implementation of the existing clamp() method at

    • https://github.com/apple/swift/blob/master/stdlib/public/core/Interval.swift.gyb

    as an example, this additional clamp() method can be implemented as

    extension ClosedInterval {
        func clamp(value : Bound) -> Bound {
            return self.start > value ? self.start
                : self.end < value ? self.end
                : value
        }
    }
    

    Example:

    (0.0 ... 5.0).clamp(4.2)    // 4.2
    (0.0 ... 5.0).clamp(-1.3)   // 0.0
    (0.0 ... 5.0).clamp(6.4)    // 5.0
    

    ClosedInterval is a generic type

    public struct ClosedInterval { ... }
    

    therefore this works not only for Double but for all types which are Comparable (like Int, CGFloat, String, ...):

    (1 ... 3).clamp(10)      // 3
    ("a" ... "z").clamp("ä") // "ä"
    

    Update for Swift 3 (Xcode 8): ClosedInterval has been renamed to ClosedRange, and its properties are lower/upperBound now:

    extension ClosedRange {
        func clamp(_ value : Bound) -> Bound {
            return self.lowerBound > value ? self.lowerBound
                : self.upperBound < value ? self.upperBound
                : value
        }
    }
    

提交回复
热议问题