假设:
let a = 4.2
let b = -1.3
let c = 6.4
我想知道将这些值限制在给定范围内的最简单,最快捷的方式,比如0...5
,这样:
a -> 4.2
b -> 0
c -> 5
我知道我可以做到以下几点:
let clamped = min(max(a, 0), 5)
或类似的东西:
let clamped = (a < 0) ? 0 : ((a > 5) ? 5 : a)
但是我想知道在Swift中是否有其他方法可以做到这一点 - 特别是我想知道(并且在SO上记录,因为似乎没有关于在Swift中钳位数字的问题)是否存在是Swift标准库中的任何内容,专门用于此目的。
可能没有,如果是这样,那也是我很乐意接受的答案。 :)
答案 0 :(得分:65)
Swift 4/5
Comparable/Strideable
的扩展名与ClosedRange.clamped(to:_) -> ClosedRange
类似于标准Swift库。
extension Comparable {
func clamped(to limits: ClosedRange<Self>) -> Self {
return min(max(self, limits.lowerBound), limits.upperBound)
}
}
extension Strideable where Stride: SignedInteger {
func clamped(to limits: CountableClosedRange<Self>) -> Self {
return min(max(self, limits.lowerBound), limits.upperBound)
}
}
<强>用法:强>
15.clamped(to: 0...10) // returns 10
3.0.clamped(to: 0.0...10.0) // returns 3.0
"a".clamped(to: "g"..."y") // returns "g"
// this also works (thanks to Strideable extension)
let range: CountableClosedRange<Int> = 0...10
15.clamped(to: range) // returns 10
答案 1 :(得分:40)
ClosedInterval类型已经有
func clamp(_ intervalToClamp: ClosedInterval<Bound>) -> ClosedInterval<Bound>
将另一个 interval 作为参数的方法。有一个 关于Swift进化邮件列表的提案
添加另一个方法,将单值钳位到给定的时间间隔:
/// Returns `value` clamped to `self`.
func clamp(value: Bound) -> Bound
这正是你所需要的。
使用
中现有clamp()
方法的实现
例如,这个额外的clamp()
方法可以实现为
extension ClosedInterval {
func clamp(value : Bound) -> Bound {
return self.start > value ? self.start
: self.end < value ? self.end
: value
}
}
示例:
(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
是通用类型
public struct ClosedInterval<Bound : Comparable> { ... }
因此,这不仅适用于Double
,也适用于所有人
类型Comparable
(例如Int
,CGFloat
,String
,...):
(1 ... 3).clamp(10) // 3
("a" ... "z").clamp("ä") // "ä"
更新 Swift 3(Xcode 8): ClosedInterval
已重命名
至ClosedRange
,其属性现为lower/upperBound
:
extension ClosedRange {
func clamp(_ value : Bound) -> Bound {
return self.lowerBound > value ? self.lowerBound
: self.upperBound < value ? self.upperBound
: value
}
}
答案 2 :(得分:15)
使用与Apple相同的语法来执行min和max运算符:
public func clamp<T>(_ value: T, minValue: T, maxValue: T) -> T where T : Comparable {
return min(max(value, minValue), maxValue)
}
你可以这样使用:
let clamped = clamp(newValue, minValue: 0, maxValue: 1)
这种方法很酷的是,任何值都定义了执行操作所必需的类型,因此编译器自己处理它。
答案 3 :(得分:9)
extension Comparable {
func clamped(_ f: Self, _ t: Self) -> Self {
var r = self
if r < f { r = f }
if r > t { r = t }
// (use SIMPLE, EXPLICIT code here to make it utterly clear
// whether we are inclusive, what form of equality, etc etc)
return r
}
虽然我在Swift中真正的爱系列,但我确实认为钳制函数的绝对标准语法(“每种计算机语言已有50年的历史了”)更简单,更好: >
x = x.clamped(0.5, 5.0)
直到它内置到Swift中,我真的认为这是最好的。
哲学角:
IMO钳位函数中的两个值实际上不是“范围” -它们只是“两个值”。
(例如:在游戏代码中,两个动态值有时处于“错误的顺序”(即所需的结果在外面)是完全相同的(在结果中只是值)。
答案 4 :(得分:6)
在Swift 3中有新的CountableClosedRange
,CountableRange
,Range
,ClosedRange
协议。它们具有相同的upperBound
和lowerBound
属性。因此,您可以通过声明自定义协议,使用Range
方法一次扩展所有clamp
协议:
protocol ClampableRange {
associatedtype Bound : Comparable
var upperBound: Bound { get }
var lowerBound: Bound { get }
}
extension ClampableRange {
func clamp(_ value: Bound) -> Bound {
return min(max(lowerBound, value), upperBound)
}
}
extension Range : ClampableRange {}
extension ClosedRange : ClampableRange {}
extension CountableRange : ClampableRange {}
extension CountableClosedRange : ClampableRange {}
用法:
(0...10).clamp(12) // 10
(0..<100).clamp(-2) // 0
("a"..."c").clamp("z") // c
答案 5 :(得分:5)
在关注@Fattie的回答和我的评论后,以下是我的建议:
extension Comparable {
func clamped(_ a: Self, _ b: Self) -> Self {
max(min(self, a), b)
}
}
答案 6 :(得分:2)
使用 Swift 5.1 ,惯用的方式是使用property wrappers。来自NSHipster的修饰示例:
@propertyWrapper
struct Clamping<Value: Comparable> {
var value: Value
let range: ClosedRange<Value>
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
precondition(range.contains(wrappedValue))
self.value = wrappedValue
self.range = range
}
var wrappedValue: Value {
get { value }
set { value = min(max(range.lowerBound, newValue), range.upperBound) }
}
}
用法:
@Clamping(0...5) var a: Float = 4.2
@Clamping(0...5) var b: Float = -1.3
@Clamping(0...5) var c: Float = 6.4
答案 7 :(得分:1)
最短(但可能不是最有效)的夹紧方式是:
let clamped = [0, a, 5].sorted()[1]
来源:用户{@ {3}}中的