
Math.min(Math.max(num, min), max) - tosh
https://twitter.com/jaffathecake/status/1296382880030044160
======
patresh
I find that the fact that the functions min and max have the same name as the
variables min and max increases cognitive load which makes it harder to think
about it.

I find the following easier to read :

    
    
      Math.min(Math.max(num, lower_bound), upper_bound)

~~~
Someone
Easy to remember, but may take some time to grasp:

    
    
      Arrays.sort( {lower_bound, num, upper_bound} )[1];
    

Next challenge: teach the optimizer to make that almost as fast as the min/max
way ;-)

(You can’t reduce it to the min/max call because it also works if you
accidentally pass a lower bound that’s larger than the upper bound. Worst-
case, the above takes 3 comparisons, unless at least two of the inputs are
constants)

~~~
chrisseaton
> Next challenge: teach the optimizer to make that almost as fast as the
> min/max way ;-)

I did _exactly_ this for my PhD!

[https://chrisseaton.com/phd/](https://chrisseaton.com/phd/)

~~~
ford_o
Wait, how is that not trivial?

~~~
chrisseaton
> how is that not trivial?

It could be trivial to implement an optimisation which does this for that
exact code. But what are you going to do? Hand-code an optimisation for every
similar thing people could write? I implemented a general solution.

So it also works through metaprogramming:

    
    
        [1, 2, 3].send(:sort).send(:[], 1)
    

Through user-defined sorting order:

    
    
        [1, 2, 3].sort_by { |a, b| b <=> a }[1]
    

When nested:

    
    
        [[1, 2].sort[1], 3].sort[0]
    

And so on.

Note that it also needs to be transparent to debuggers and profilers, it needs
to handle multiple method redefinitions (for example what happens if someone
redefines the sorting order for integers).

It's not a pattern-matching optimization - it's partial evaluation enabled by
a new kind of polymorphic inline cache.

~~~
azinman2
> But what are you going to do? Hand-code an optimisation for every similar
> thing people could write?

While I appreciate that you solved the general problem, I wonder if there are
legs here. Specifically, could one mine GitHub to find automatically common
patterns that one could write specific optimizers for, or at minimum, leverage
that to learn what semi-general cases are worth optimizing? To my knowledge
optimizing compilers already do have effectively handlers for common
operations, but I don’t know if anyone has leveraged “big data” to help guide
this.

IIRC, various tweaks and optimizations in Java were guided by Sun analyzing
their own code based. GitHub is just so much bigger, and polyglot.

------
donquichotte
Luckily, this is a solved problem for go.

    
    
      func helper(a float64, c chan float64){
          time.Sleep(time.Duration(a) * time.Second)
          c <- a
      }
      
      func clamp(a float64, min float64, max float64) float64 {
          c := make(chan  float64, 3)
          go helper(a, c)
          go helper(min, c)
          go helper(max, c)
          _, out, _ := <-c, <-c, <-c
          return out
      }

~~~
ccmcarey
That will give you the median value. What OP wants is the value `a` clamped
within `min` and `max`.

~~~
donquichotte
Can you give an example where median([a, min, max]) is not equal to clamp(a,
min, max), given max >= min?

~~~
naikrovek
If a < min or a > max, if I'm reading it right.

~~~
mc10
You can prove that this algorithm works for both of those cases. Assuming min
<= max:

If a < min, then the sorted array is [a, min, max]. The median is min, which
is a clamped to min.

If a > max, then the sorted array is [min, max, a]. The median is max, which
is a clamped to max.

------
Const-me
In languages I use there’s usually no need to write that code.

C++/17 has std::clamp() in <algorithm> header.

Modern C# has Math.Clamp() since .NET Core 2.0; too bad it’s not available in
desktop edition of the runtime.

HLSL has clamp() intrinsic function, and a special version saturate() to clamp
into [ 0 .. +1 ] interval.

~~~
f00zz
It gets a bit confusing when the order of arguments is different depending on
the library. For instance, with std it's std::clamp(val, min, max), but with
Qt it's qBound(min, val, max) (for some reason I think the order of arguments
in qBound is more logical).

~~~
Someone
The expression _Math.min(Math.max(num, min), max)_ is symmetric in _min_ and
_num_ , so it doesn’t matter whether you interchange _min_ and _num_ (or, for
that matter, _max_ and _num_ , but that is harder to see from that way to
define the ‘clamp’ function)

~~~
f00zz
Good point, thanks.

(Though I've just checked gcc's definition of std::clamp and it looks like

    
    
        __glibcxx_assert(!(__hi < __lo));
        return (__val < __lo) ? __lo : (__hi < __val) ? __hi : __val;
    

so order of arguments does matter there.)

------
mauricio
Always good to know the underlying implementation, but for any Ruby readers
check out clamp(). It's been available since 2.4.

    
    
      25.clamp(5, 10)
      => 10
    
      6.clamp(5, 10)
      => 6
    
      1.clamp(5, 10)
      => 5

~~~
chrisseaton
I often see [a, b, c].sort[1] which I think is very neat.

~~~
nitrogen
If you are writing code that is clamping a lot of numbers, the GC churn from
building a new array every time might be a problem.

~~~
chrisseaton
Ideally the temporary array should optimise away.

~~~
nitrogen
Ideally, but so far my experiences with simple benchmarks and with ruby-prof
have shown that CRuby doesn't make such optimizations:

[https://repl.it/repls/GargantuanThistleLink](https://repl.it/repls/GargantuanThistleLink)

    
    
        Result from sort: 3 in 0.9785124980007822s
        Allocated 3000001 object(s)
        Result from ternary: 3 in 0.3205206830025418s
        Allocated 1 object(s)
        Result from clamp: 3 in 0.5030354310001712s
        Allocated 2 object(s)
    

Interestingly the ternary comparison is faster than clamp.

------
rewq4321
Stage 1 ECMAScript proposal to add Math.clamp (among others):
[https://github.com/rwaldron/proposal-math-
extensions](https://github.com/rwaldron/proposal-math-extensions)

But it looks dead: [https://github.com/rwaldron/proposal-math-
extensions/issues/...](https://github.com/rwaldron/proposal-math-
extensions/issues/16)

As someone mentioned in the thread, nested ternary is easier to interpret:

(a > max ? max : (a < min ? min : a))

~~~
macspoofing
To each his own but I disagree. Nested ternary's are hard to read and
understand, and modifying them (by future devs) is tricky and error prone.

~~~
Stratoscope
True, _nested_ ternaries can be hard to follow. And the excessive parentheses
promote this way of looking at it.

OTOH I think _chained_ ternaries can be simple and easy to understand.

Yes, they are the exact same thing in this case, but getting rid of those
nested parens really helps, at least for me.

sarah180's example is a good illustration. I would change the order of the
tests because it makes more sense to me to check the min before the max. I'd
also make one minor formatting change, because I code in a proportional font
and can't line things up in columns:

    
    
      a < min ? min :
      a > max ? max :
      a
    

Maybe people think differently, but to me that is super easy to understand,
and _much_ better than the confusing Math.min/max stuff.

I would also wrap the whole thing inside a function:

    
    
      function clamp( value, min, max ) {
          return(
              value < min ? min :
              value > max ? max :
              value
          );
      }
    

Now that it's inside a function, you could change the code to use if
statements, or Math.min/max, or whatever suits your preferences.

------
tzs
68K assembly, no branches:

    
    
      # d0 = num, d1 = low, d2 = high, d3 = scratch
      sub.l   d1, d0
      subx.l  d3, d3
      or.l    d3, d0
      addx.l  d1, d0  # d0 = max(num,low)
      sub.l   d2, d0
      subx.l  d3, d3
      and.l   d3, d0
      add.l   d2, d0  # d0 = min(max(num,low),high)
    

Adapted from "Superoptimizer -- A Look at the Smallest Program" by Henry
Massalin [1].

[1]
[https://web.stanford.edu/class/cs343/resources/superoptimize...](https://web.stanford.edu/class/cs343/resources/superoptimizer.pdf)

~~~
saagarjha
Note that with modern branch predictors such optimizations may not actually be
beneficial–ARM got rid of condition codes on all its instructions in the
64-bit version. (Plus, I assume they ran out of space to encode them.)

~~~
brandmeyer
IEEE754 has defined min and max for quite some time. Nowadays almost all FPUs
have min and max instructions built-in.

------
geophile
OPs implementation requires 2 comparisons. I think this C expression is
clearer, and will sometimes use 1 comparison:

    
    
        num < min ? min :
        num > max ? max :
        num

~~~
edflsafoiewq
Incidentally, gcc and clang both compile this to two cmovs for me.

~~~
geophile
I don't speak x86. Do you mean that these compilers are evaluating both
conditions, all the time? At what optimization level?

~~~
benchaney
x86 has instructions that execute conditionally. Most conditionals in higher
level languages get compiled to conditional jumps, but using conditional
operations this isn't necessary. The same code path is taken in all cases, and
the instructions effect is what is conditional.

In the case of cmov, it is either a nop or a mov dependent on the state of the
conditional flags. Using this construct instead of a regular mov guarded by
conditional jumps has better performance in some cases.

On my machine gcc is outputting a combination of conditional jumps and
conditional movs at all optimization levels

------
V-2
Kotlin provides pretty nice syntax sugar for that:

    
    
       num.coerceIn(min..max)
    

That's it. This human reader finds it considerably more readable.

It also has

    
    
       coerceAtLeast
    

and

    
    
       coerceAtMost

~~~
nitrogen
Does kotlinc optimize away inline ranges like that, or does this result in a
range object being constructed and discarded?

~~~
RandomBK
I haven't heard of any instances of Kotlin itself optimizing these things
away, but the JVM may be able to do so during its various JIT passes. It's
definitely not something you can necessarily rely on, though.

Luckily, these convenience methods are usually implemented as inline extension
functions, so the whole thing will get inlined into the calling method, making
JIT optimization more likely.

------
tobr

      [min, num, max].sort()[1]

~~~
tosh
sort() on JavaScript arrays sorts alphabetically (unless you pass a
compareFunction)

[https://stackoverflow.com/questions/21019902/why-cant-
javasc...](https://stackoverflow.com/questions/21019902/why-cant-javascript-
sort-5-10-1)

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)

~~~
yesbabyyes
Good point, for those who need to implement a slick compareFunction for
numbers, Math.sign is great:

    
    
      [min, num, max].sort((a, b) => Math.sign(a - b))[1]
    

An entirely different alternative is poor man's match:

    
    
      switch (true) {
      case num > max: return max;
      case num < min: return min;
      default: return num;
      }

~~~
recursive
Why sign though? I always write my sorts like this.

    
    
        .sort((a, b) => a - b)

~~~
saagarjha
Is there a better way to grab a reference to the subtraction operator?

~~~
recursive
I assume you're referring to a point-free reference.

Only by defining a `const subtract = (a, b) => a - b;`, which isn't really
point-free.

Or by using a different language.

------
O_H_E
I think the main hurdle is that we need to read this inside-out, which is
something we face regularly trying to read nested function calls. I seriously
believe pipe-like operators solve this problem cleanly.

In Elixir which does have the pipe `|>` this could be.

    
    
        number = number |> max(lower_bound) |> min(upper_bound)
    

Which IMO is quite elegant.

~~~
O_H_E
Note: I don't really know Elixir, all I know is that this works in repl.it

~~~
jakeva
I use Elixir day to day, and I agree, piping is one of the nicest things about
it. And your code is the way I would have written it.

------
edparcell
It is simpler if you use the notation [x]_a^b (i.e. with a subscript and a
superscript b) to mean x, clipped to the range a to b, and skip writing +/\-
infinity if you don't intend clipping on one side.

Then you get a bunch of obvious identities like [x]^b = min(x, b) = [b]^x (x
capped by b is the same as the smaller of x and b which is the same as b
capped by x), [x]_a^b = [b]_a^x, and [x]_a^b = [[x]_a]^b. Putting these
together you get [x]_a^b = [[x]_a]^b = min(max(x, a), b). But honestly it's
just easier to stick to the notation most of the time.

A better write-up, for everyone who doesn't like reading new math notations
inline: [https://imgur.com/gallery/593QEow](https://imgur.com/gallery/593QEow)
(Imgur link with white background)
[https://quicklatex.com/cache3/71/ql_46c49ac709b3789482d0736d...](https://quicklatex.com/cache3/71/ql_46c49ac709b3789482d0736d80481871_l3.png)
(Original link - renders badly in Chrome due to PNG transparency)

~~~
secondcoming
Those are all valid C.

------
css
Very coincidental I see this post almost immediately after writing the same
code:

    
    
        new_poll_rate = \
            min(
                max(
                    1 / messages_per_second,
                    constants.FASTEST_POLL_RATE
                ),
                constants.SLOWEST_POLL_RATE
            )
    

I agree with the sentiment, I had to re-read this several times to make sure I
got it right.

~~~
bovine3dom
If fastest poll rate > slowest poll rate, I think you've got them the wrong
way around (or is that the joke?).

~~~
css
FASTEST_POLL_RATE is 1000hz, SLOWEST_POLL_RATE is 10hz. Thus,
FASTEST_POLL_RATE is a smaller number on a per-second basis.

~~~
bovine3dom
Ah, I see - for clarity I'd rename them FASTEST_POLL_RATE ->
SHORTEST_POLL_PERIOD or store them in Hz rather than seconds, so everything
was 1/ in that little snippet. Thanks for clearing up my confusion :)

~~~
css
Yeah that’s a very good call, I will probably rename these. Thanks for the
critique!

------
9nGQluzmnq3M
Not as elegant, but way more readable:

if num > max: num = max

if num < min: num = min

------
gre
In Factor:

    
    
        : clamp ( x min max -- y ) [ max ] dip min ; inline
    

[https://docs.factorcode.org/content/word-
clamp,math.order.ht...](https://docs.factorcode.org/content/word-
clamp,math.order.html)

~~~
cjallen88
For casual readers, `dip` pops the top of the stack, executes a quotation,
then pushes the top of the stack back on.

So this pops the max value off the stack, applies the quoted `max` word to the
x and min stack values, then pops the max value back on the stack and applies
the `min` word to the result and the max.

------
STRML
The basic function is simply defined as:

    
    
      function clamp(num, min, max) {
        return Math.max(min, Math.min(num, max));
      }
    

That is, if you don't try to do anything fancy and make any parameters
optional. Lodash does and it makes the implementation much more complex.

    
    
      _.clamp(input: number, lower?: number, upper: number): number;
    

[https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb7...](https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb70ec9973b66baec0975/lodash.js#L13981)

~~~
bonoboTP
Or to make sure it's crystal clear what's going on:

    
    
        function clamp(num, min, max) {
          if (num > max)
            return max;
          if (num < min)
            return min;
          return num;
        }

~~~
52-6F-62
Speaking only to JS is there any reason to write it any other way outside of
being clever or as a lambda for singular use? I definitely prefer this
version. (Assuming any necessary runtime checks are included for a given
project)

~~~
beirut_bootleg
There are many reasons to forego readability, especially when writing a
library: performance, compatibility, requirements, interpreter/compiler
optimizations or even cyclomatic complexity.

In lodash's case it might even be all of the above, although I can't speak for
the intentions of the authors since there are no comments to guide readers
through the process.

Note GP's link points to what looks like the v3 branch. Check out the latest
implementation of clamp, with a few less if statements, and what looks like a
NaN check using strict equality if you want your mind blown.
[https://github.com/lodash/lodash/blob/86a852fe763935bb64c125...](https://github.com/lodash/lodash/blob/86a852fe763935bb64c12589df5391fd7d3bb14d/clamp.js#L22-L23)

~~~
robocat
Reading that code it looks to me that:

clamp(null) returns 0

clamp(undefined) returns NaN

clamp(1, NaN, NaN) returns 0

clamp(1) returns 0

clamp(1, 5, NaN) returns 5

JavaScript is hard to write safe code for.

------
wonderlg
There’s an obvious solution to this:

    
    
        require('clamp')(num, min, max)
    

1 million monthly downloads

------
OscarCunningham
Ideally I'd also want to throw an error when max < min.

------
sanbor
Kotlin implementation [1]:

    
    
        public fun Int.coerceIn(minimumValue: Int, maximumValue: Int): Int {
            if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.")
            if (this < minimumValue) return minimumValue
            if (this > maximumValue) return maximumValue
            return this
        }
    

[1]
[https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/c...](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/coerce-
in.html)

------
ridiculous_fish
Most of the ternary implementations here are less correct than the original.
You generally want min/max to satisfy the following:

1\. min/max of NaN and anything is NaN

2\. negative 0 is strictly smaller than positive 0

it's a fun exercise to implement clamp under these constraints.

------
slifin
I'm not sure why the author isn't putting his preferred implementation into a
clamp function then forgetting about it?

------
kaiken1987
An amusing anecdote ruined by the fact that everyone who can relate
immediately starts thinking of a better way to rewrite it. Yes brain that
might be better but that's not the joke.

------
throwawaw
Fun seeing that pretty much everyone else finds that idiom confusing too.
Half-serious, over breakfast:

    
    
        (case [(> n min) (< n max)]
          [true true] n
          [true false] max
          [false true] min)
    

(Side note: Clojure's `>` and `<` are kind of unreadable to begin with.
Turning `if (> n min)` into "if n is greater than min" takes some work for me,
still, after more than a year.)

~~~
retzkek
However, prefix notation becomes much easier to read and reason about when you
have more than two things to compare:

    
    
        (> 3 2 1 ...)
    

vs

    
    
        (3 > 2) && (2 > 1) && ...

------
fouric
Do people actually have trouble with this repeatedly, or just when they first
learn about it? I started using this implementation of clamp a few years ago,
and while it gave me some trouble when I first implemented it, the pattern is
very simple, and I got used to it very quickly.

I use Common Lisp:

> (max min (min max n))

Is the difference my choice of language, my personal mental hardware, the
amount of familiarity one has with the pattern, or none of the above?

------
tosh
in k:

    
    
      xs: 1 2 3 4 5
      max: 4
      min: 2
    
      max& min| xs
      2 2 3 4 4
    

works for scalar, vector, matrix

~~~
uryga
K made me realize that if you use the numbers 0 and 1 for booleans, `min()` is
`&&` and `max()` is `||`. love that!

~~~
tosh
same here, illuminating a-ha moment!

------
sujitnair
I find the following easier to visualize. If L is the lower bound and U is the
upper bound, and if you visualize L, U as two points on the real number line,
then:

left_of_U ( right_of_L (num)) = right_of_L ( left_of_U (num)) = clamped
version of num between L and U.

Here, left_of_U = Math.min(num, U) and right_of_L = Math.max(num, L).

------
Imnimo
On the one hand, I've done this enough times that I can usually write it on
auto-pilot without thinking about it. But on the other hand, whenever I make a
mistake, the resulting bug is really hard to track down, because it's tough to
just stare at the line and recognize that something wrong.

------
jodrellblank
In APL:

    
    
        lower⌈ upper⌊ numbers
    

See a stream of random numbers flowing from right to left. See the higher ones
being pushed down ⌊ to the upper bound, and the lower ones being pushed up ⌈
to the lower bound, and the middle ones flowing through both guards unchanged.

------
ossopite
Excel has no clamp function but a neat alternative is

    
    
        =MEDIAN(min, num, max)

------
ChrisLomont
I find the following much easier to read. It's certainly easier to maintain,
deal with, for successive programmers, and likely has exactly the same
compiled/interpreted operations for most common languages. Optionally replace
t1 and t2 by overwriting num if needed.

Use full if else if you must.

If you're worried about speed, using built in min and max is not a good idea.
There are many, many tricks to remove branches for certain datatypes, etc., as
you need.

var t1 = num < min ? min : num; // clamp to min var t2 = max < t1 ? max : t1;
// clamp to max return t2; // num clamped to [min,max]

------
migueloller
I like doing `Math.max(min, Math.min(num, max))` since it puts them in sort
order.

------
HaGoijer
Slightly offtopic, but very practical use of a comparable algorithm is to
determine to what extent a date ranges falls between a first and last date
(e.g. first date of the year, last date of year). e.g. in Excel it would look
like: MAX((MIN(end date range; last date) - MAX(Start date range;first date) +
1);0)/(last date - first date + 1). This results in 0 in case of no match, a
fraction when only a part of the date range falls between the first and last
date, and 1 in case it matches completely or even exceeds the first and last
date.

------
w-m
I personally would find this much more straightforward:

    
    
      val - (sign(val - max) + 1) / 2 * (val - max) + (sign(min - val) + 1) / 2 * (min - val)

------
jchw
I always used to get confused by the function names “min” and “max” because
they return the minimum and maximum, but typically when you use them you are
thinking in terms of _applying_ a minimum or maximum bound. Having a dedicated
clamp function significantly helps although imo there is not a single correct
order for the parameters to go in.

------
todorov84
For maximum readability, you could also go for

    
    
      (min + ((num + max - Math.abs(num - max)) / 2) + Math.abs(min - ((num + max - Math.abs(num - max)) / 2))) / 2
    

Since

    
    
      min(a, b) = (a + b - |a - b|) / 2
    

and

    
    
      max(a, b) = (a + b + |a - b|) / 2

------
kazinator

      This is the TXR Lisp interactive listener of TXR 242.
      Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for  cheatsheet.
      1> (clamp 1 10 -1)
      1
      2> (clamp 1 10 15)
      10
      3> (clamp 1 10 5)
      5
    

Added on August 13, 2015 by commit f2e197dcd31d737bf23816107343f67e2bf6dd8e

------
forth_fool
Talking about obscurity of code, in Forth you would write:

    
    
      5 9 7 min max .
    

This prints 7, as it's between 5 (lower bound) and 9 (upper bound).

Defining a _clamp_ "function" looks like this:

    
    
      : clamp ( min max n -- clamped_n )
        min max ;

------
parliament32
Python also doesn't have a built-in clamp function (I think there might be one
in numpy), I usually use

    
    
        sorted((floor, x, ceiling))[1]
    

Throw it in a function with some asserts if you're worried about x not being a
number and giving you weird results.

~~~
doukdouk
There is indeed one in numpy :
[https://numpy.org/doc/stable/reference/generated/numpy.clip....](https://numpy.org/doc/stable/reference/generated/numpy.clip.html)

------
aaron695
> it takes me ages to convince myself this implementation is correct.

So never use it in code.

------
spapas82
Here's clamp in idiomatic Elixir (using multi-clause functions and guards):

    
    
      def clamp(min, _max, n) when n<min, do: min
      def clamp(_min, max, n) when n>max, do: max
      def clamp(_min, _max, n), do: n

~~~
grantjpowell
An Elixir convention I've seen is to put the thing you're operating on first,
so that you can compose functions using the `|>` operator, which places the
previous expression as the first argument of the function to the right.

Maybe something like this?

    
    
      defmodule Compare do
        def clamp(number, minimum, maximum) do
          number
          |> max(minimum)
          |> min(maximum)
        end
      end
      
      import Compare
      
      clamp(5, 1, 10) # 5
      clamp(1, 5, 10) # 5
      clamp(10, 1, 5) # 5
      
      
      some_number
      |> clamp(min, max)
    
    

As a side note, I think the Elixir |> operator is a stroke of genius that
other languages should take a look at. Making the pipe operator append the
_first_ argument has the following benefits

1.) It makes the most "important" argument of the function the first thing you
read in function signatures

2.) If you need to add more arguments to a function signature later, they tend
to be less important the original args, so they tend to make sense at the end

3.) It creates a convention for all libraries to follow so they can leverage
the pipe operator. Its really jarring when the thing you want to put in a
pipeline isn't the first argument (looking at you `Regex`[0] which puts the
regular expression as the first arg and not the string)

[0]
[https://hexdocs.pm/elixir/Regex.html#replace/4](https://hexdocs.pm/elixir/Regex.html#replace/4)

~~~
LandR
> It makes the most "important" argument of the function the first thing you
> read in function signatures

Doesn't that make writing functions that can use partial application harder?
e.g. If I was writing clamp i would want the signature to be

    
    
         (defn clamp [min max n] ,,,)
    

Then I can do:

    
    
        (map (partial clamp 1 11) [-14 2 5 8 11 15 18])
    

I know when I use Clojures threading macros I use thread last way more than
any of the others. My next most common would be piping it into arbitrary
locations, e.g.:

    
    
        ; pipe into an arbitrary spot (specified here as o)
        (as-> (range 1 10) o
              (map inc o)
              (filter even? o)
              (reduce + o))
    
    

I rarely use thread-first.

~~~
grantjpowell
Elixir has an anonymous function short hand that makes the "partial
application" use case pretty easy for me.

    
    
      &Compare.clamp(&1, some_min, some_max)
    

The above creates an arity one function which puts the arg into the first
position in the original function.

I've never felt pain around partial application because I use the anonymous
function short hand a good deal

------
da39a3ee
[https://twitter.com/ben_a_adams/status/1296463312780251136](https://twitter.com/ben_a_adams/status/1296463312780251136)

------
occamrazor
Numpy has `np.clip`.

In pure Python `statistics.median([min, max, num])` also works.

------
euske
In Python, I usually put the following aliases:

    
    
      upperbound = min
      lowerbound = max
    

Use:

    
    
      a = upperbound(a, 10)
      b = lowerbound(-10, b)

------
polote
> it takes me ages to convince myself this implementation is correct.

> Googler

Then this is another proof that google doesnt only hire on mathematical puzzle
tricks

------
chewbaxxa
Math.min(a, x) == at_most_a(x)

Math.max(b, x) == at_least_b(x)

at_most_b(at_least_a(x))

A similar kind of thing is really common in measure theory (limsup, liminf)
and option payoffs.

------
antoineMoPa
When making glsl shaders, there's clamp().

------
sktguha
I made a simple clamp function based on this const clamp = (num,
min=0,max=100) => Math.min(Math.max(num, min), max)

------
lalaithion
This is a great example of how haskell makes these things obvious ;)

    
    
        clamped = num `max` min' `min` max'

~~~
graham_paul
no idea what's going on there, I know some of those variables are actually
functions but the whole thing is unreadable unless you have experience in
haskell imo

~~~
masklinn
The backticks turn regular functions into left-associative operators of the
highest priority (by default).

So

    
    
        a `foo` b `bar` c
    

is

    
    
        (bar (foo a b) c)

~~~
O_H_E
Oohh nice. So kinda like a pipe.

I really think we deserve more syntax that allowed something like this,
because otherwise one needs to read `(bar (foo a b) c)` inside-out.

~~~
masklinn
> Oohh nice. So kinda like a pipe.

I never thought of it that way, but it is true that it can be used as a pipe.

Fundamentally it's just the dual of (): Haskell lets you use operators as
infix functions by wrapping them in () so

    
    
        (+) 1 2
    

is the same as

    
    
        1 + 2
    

and conversely lets you use prefix (binary) functions as operators by wrapping
them in backticks.

You can even combine those through _sections_ , which serve to partially apply
infix operators:
[https://wiki.haskell.org/Section_of_an_infix_operator](https://wiki.haskell.org/Section_of_an_infix_operator)

------
voldacar
The fact that you need to type 33 characters to define something so simple as
clamp() really does not make sense

------
throwaway6734
This is something that upon seeing I would sketch out. It's way easier for me
to understand visually

------
ragnese
This one doesn't bother me much. What's harder and worse, IMO, is doing
saturating _math_.

------
aldanor
Once you've seen this a few dozen times, you instantly parse it as "clamp(num,
min, max)"

~~~
hombre_fatal
Did a bunch of coding bootcamps just begin session or something? I don't
understand the comments here treating it like it's so hard to write and verify
that it needs a completely different, slower, less direct, cutesy
implementation.

~~~
aldanor
I don't understand the explosion of comments here either. I was just saying
for myself, it's a trivial construct and very commonly encountered.

------
beervirus
Is this a problem that comes up often? What kind of situation would make you
want to calculate this?

~~~
mr_toad
It’s pretty common in data science or financial applications to restrict the
range of data in this way.

There are probably SQL devs reading this and wondering what the fuss is about.

------
rkagerer
Wrap it in a function, verify the implementation once, then henceforth use
clamp(x, min, max).

------
pachico
I would make it more confusing to read by using the 100 and 0 percentiles.

------
O_H_E
I always though pipe operators are under-rated and under-used.

In elixer that could be

------
ebg13
> _it takes me ages to convince myself this implementation is correct_

It would take them less time to convince themself if they switched min and num
to put the values in proper semantic order: min < num < max.

Math.min(Math.max(min, num), max)

~~~
bhaak
That still leaves the order of Math.min and Math.max undecided and will
probably not help much if you get easily confused by the visuals of this code.

I never thought about it but of course there must be code tongue twisters (or
more correctly, brain twisters).

Thinking about it, I would probably go with a less confusing implementation.
Terse code is hard to read and the compiler is likely clever enough to choose
the best implementation anyway.

~~~
hansvm
> and the compiler is likely clever enough to choose the best implementation
> anyway.

Not in my experience. The compiler is likely to be able to do something decent
to it, but it'll probably be different.

Consider the following snippets. For unsigned integers they're all equivalent
(and return the max of x and m), but gcc with a wide range of flags can't
recognize them as identical.

    
    
      (x<m) * m + (x>=m) * x
    
      (x<m) * (m-x) + x
    
      x<m ? m : x

~~~
bhaak
There can always be things that trip the compiler. Without looking at the
actual assembler, you never know that.

Interestingly in your example, clang is able to resolve the first and the
third line to the same assembler code.

[https://godbolt.org/z/navMEc](https://godbolt.org/z/navMEc)

I do wonder why it then isn't able to do that for the second line. Possibly
because the CPU might have different flags set after processing the
substraction?

But I'm not sure if your example is a good argument after I said I prefer less
confusing code and you present an example which even confuses the compiler.
;-)

------
moralestapia
>[...] it takes me ages to convince myself this implementation is correct.

Is that supposed to be difficult?

Not trying to be snarky, I'm honestly surprised, do people actually struggle
with this? Googlers in particular?

~~~
dgb23
This whole discussion feels like I‘m not getting an obvious joke.

------
aabbcc1241
Just write a isBetween(low,middle,high)

~~~
abiogenesis
Your method name and the signature are misleading. First, it implies that it
returns a boolean, not a number. Second, the argument names look like they
have to be already sorted, which defeats the purpose.

