The reason for adding these two methods is that the current implementation for applying a limit to a value is not straightforward enough to read. We have a couple of options if we want to implement this logic:
requirement: to limit the variable a to be at least greater than 5
var a = 5
a = max(6, a)
var a = 5
a = a < 6 ? 6 : a
var a = 5
if a < 6 {
a = 6
}
I feel none of the above expressions express clearly "a must be greater than or equal to 6", but a = a.applyingThreshold(6) sounds more clear.
In the function proposed, it's not clear to me that "Cap" is related to min and "Threshold" is related to max. It's easier for me to understand the max and min functions already present. But maybe I'm just strange
I feel like you will usually be capping the value both from the bottom and top, so it should be a one method taking a range. That way you can have a nice, unambiguous name "clamp"
a.clamp(to: .min...6) // in place
let b = a.clamped(to: 0...6) // returning a new number
You could also overload this with partial ranges for the cases where you don't want to clamp both ends - then you'd have a consistent syntax for all such operations:
a.clamp(to: ...6) // equivalent to a = min(a, 6)
a.clamp(to: 5...) // equivalent to a = max(a, 5)
I find min() and max() for clamping are like using </<=/>=/> to compare whether one date is before another one - they both make sense, but in certain situations it isn't intuitive and there's additional cognitive load over x.clamp(to:). Clearly if you want to find the max of two values, max makes sense, but if you want to, for example, limit the x coordinate of a sprite:
As several others have noted, clip and clamp are the standard terms of art for this operation. (clip is used more in signal processing, clamp is used in ~every shader and compute language).
Hi all, adding clamp to the standard lib is also something I'm interested in but looking at the latest posts about it and attempting to revive discussion over at the evolution post [Revision] Fixing SE-0177: Add clamp(to:) to the stdlib it doesn't seem to be moving anywhere unfortunately.
It seems the consensus was to try and go for a protocol oriented approach, however that ran in to some difficulties.
Having played around it with it myself I think there's definitely some design issues that need working out. Specifically, the case of Int types and their Range and PartialRangeUpTo implementations have an unfortunate edge case which is, if you specify a Range/PartialRangeUpTo where the upper and lower bounds (explicit or implicitly in the case of PartialRangeUpTo) are equal, then there is the risk of a runtime error. What's more, this will happen pretty frequently, and in the case of using clamping(_:) to 'safely' access elements of an array, a zero length array will cause the very runtime error that was meant to be avoided!
Of course, one could guard against this and just return the lower bound, but this seems imprecise.
With that in mind, I would avoid implementation of clamped for Range/PartialRangeUpTo specifically for Ints, and I would agree that without support for the full set of ranges, they shouldn't be supported at all.
Here, I would vote to falling back to a simple clamped(min:,max:_) free function to sit alongside min(_:_:) and max(_:_:) in the stdlib.
I do believe there is a case for a clamped function for FloatingPoint types, however. This is able to fit the full range of RangeExpression types maintaining mathematical integrity (as far as I can tell). It also seems to gel well with the frequent need/use for clamped functions in mathematical and computer graphics applications. It also simplifies the implementation.