4 ms·
We need a function "clamp" which pins a value to within two others. clamp(a,b,c) One way of writing this is: clamp(x,min,max) { return x < min ? min
by geocar 9y ago
We need a function "clamp" which pins a value to within two others.
clamp(a,b,c)
One way of writing this is:
clamp(x,min,max) { return x < min ? min : x > max ? max : x; }
and this is a fine way to do it, but sometimes people forget the order of the arguments, so they feel the need to invent an IDE to help them remember.
Another way is writing:
clamp(min,x,max) { return x < min ? min : x > max ? max : x; }
which has the advantage of "visually" putting the unknown between two values. That can help people remember which argument goes where.
Yet another way is:
clamp(min,max,x) { return x < min ? min : x > max ? max : x; }
That one is popular amongst ML, Haskell, F# etc, programmers because they have partial application. They write something like:
let clampVolume = clamp 0 11
and pat themselves on the back because now they have a function for clamping volumes.
Another reason this one is popular is because the last expression is likely to be the biggest expression. That might pay dividends on keeping bugs out.
Something I'd like people to consider is simply taking the middle value.
function clamp(a,b,c) { return [].slice.call(arguments,0).sort()[1] }
function clamp($a,$b,$c){ $x=func_get_args();sort($x);return $x[1]; }
clamp(A,B,C) -> lists:nth(2, lists:sort([A,B,C])).
sub clamp { @_ = sort @_; $_[1] }
clamp:{*x@&1=<x:(x;y;z)};
clamp:{asc[(x;y;z)]1}
This supports any crazy rationale you might have for a particular order, and it is resistant to a lot of bugs you're likely to make since the code isn't particularly repetitive no matter what language you use. Oh and it doesn't have any comparison operators either.
The only real downside is that it's slow.
Oh well.
Before we go and fix the compilers to recognise these contortions as a "clamp" function and optimise for it, maybe it's still worth meditating on next time you have a bunch of conditions in your code.
- whatshisface 9y agoGLSL (OpenGL Shading language, for GPUs) implements this language feature[0]. They chose clamp(x,min,max). Although, it's not exactly a language feature, because I think most GPUs implement this instruction directly in hardware. https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/clamp.xhtml https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/cl...
- dyarosla 9y agoSome languages let you spell out the arguments upon calling a function. clamp(x:3, min:1, max:5) Oh look! No mental strain required to think about what the function does nor what the order should be to minimize mistakes; it’s clear as day! I wish more languages adopted the option to do this.
- dyarosla 9y agoMoreover, you could also allow for something like passing a structure of parameters. clamp({min:2, max:5, x:3}) ^parameters can be rearranged as you see fit There! Now the programmer can decide what’s the way they want to call the function that makes the most sense to them.
- dvlsg 9y agoIf you use named arguments, can't you typically rearrange the order? Don't get me wrong, I use destructured arguments in js and it works fine, but I thought named arguments in other languages solve that particular issue as well.
- joshvm 9y agoIn languages like Python where you can have keyword args (typically with defaults), these options must come after the first argument, e.g. clamp(x, min=0, max=inf). Then you can do what you like with the order. And often it's good to specify the keyword for your own sanity later, when you have no idea what the third argument to some obscure Numpy call is. Or if you're using something like matplotlib and there are so many kwargs that it's better to specify. Although in this case min/max probably shouldn't have defaults, I think clamp(min, x, max) would be a bit weird in Python?
- Maken 9y agoDo you happen to work for the C++ committee? http://en.cppreference.com/w/cpp/algorithm/clamp http://en.cppreference.com/w/cpp/algorithm/clamp
- thaumasiotes 9y ago> Something I'd like people to consider is simply taking the middle value. > This supports any crazy rationale you might have for a particular order, and it is resistant to a lot of bugs you're likely to make since the code isn't particularly repetitive no matter what language you use. Oh and it doesn't have any comparison operators either. Most of your examples seem to call a "sort" function. Are you sure you're not using any comparisons?
- geocar 9y agoThe thing to focus on in the mental load. What are you more likely to get right? a = sort(x,y,z); return a[1] or something like this? if(x >= y && x =< z) return x; if(y >= x && y =< z) return y; if(z >= x && z =< y) return z; if(x >= z && x =< y) return x; if(y >= z && y =< x) return y; if(z >= y && z =< x) return z; …
- thaumasiotes 9y agoreturn max(low, ( min(val, high) )); Still hard to avoid comparisons.
- nhaehnle 9y agoFun fact: the Radeon GCN ISA actually has an instruction for taking the middle value, aka median, called v_med3.
- cesarb 9y agoAnother option would be x.clamp(min, max)
- geocar 9y agoSure, and another option would be for a range "class" to do the clamping: Range(min, max).clamp(x) but this turns to a conversation about syntax, and not about designing API so that we guide our users towards success.