40

I get confused over min and max functions, in certain contexts.

In one context, when you're using the functions to take the greater or lesser of two values, there is no issue. For example,

//how many autographed CD's can I give out?
int howManyAutographs(int CDs, int Cases, int Pens)
{
    //if no pens, then I cannot sign any autographs
    if (Pens == 0)
        return 0;

    //I cannot give away a CD without a case or a case without a CD
    return min(CDs, Cases);
}

Easy. But in another context, I get confused. If I'm trying to set a maximum or minimum, I get it backwards.

//return the sum, with a maximum of 255
int cappedSumWRONG(int x, int y)
{
    return max(x + y, 255); //nope, this is wrong
}

//return the sum, with a maximum of 255
int cappedSumCORRECT(int x, int y)
{
    return min(x + y, 255); //much better, but counter-intuitive to my mind
}

Is it inadvisable to make my own functions as follows?

//return x, with a maximum of max
int maximize(int x, int max)
{
    return min(x, max);
}

//return x, with a minimum of min
int minimize(int x, int min)
{
    return max(x, min)
}

Obviously, using the builtins will be faster but this seems like a needless microoptimization to me. Is there any other reason this would be inadvisable? What about in a group project?

17
  • 72
    If min and max are hurting your readability, consider swapping then out for regular "if"s. Sometimes it's worth to write a bit more of code to have better readability.
    – T. Sar
    Commented Jun 8, 2016 at 19:35
  • 23
    with one short post you have destroyed the whole concept of "clean coding". I salute you sir!
    – Ewan
    Commented Jun 8, 2016 at 19:37
  • 21
    Perhaps you can consider the C++11 std::clamp function or something similar.
    – rwong
    Commented Jun 8, 2016 at 19:56
  • 15
    Perhaps better names would be up_to (for min) and at_least (for max)? I think they convey the meaning better than minimize, etc. although it may take a moment's thought to realise why they're commutative.
    – Warbo
    Commented Jun 9, 2016 at 1:23
  • 59
    min and max and also minimize and maximize are totally wrong names for the functions you want to write. The default min and max make much more sense. You actually ALMOST got the function names right. This operation is called clamping or capping and you have written two capping functions. I'd suggest capUpperBound and capLowBound. I don't have to explain to anyone which one does which, it's obvious.
    – slebetman
    Commented Jun 9, 2016 at 3:57

15 Answers 15

120

As others have already mentioned: don't create a function with a name that is similar to that of a builtin, standard-library or generally widely used function but change its behavior. It is possible to get used to a naming convention even if it doesn't make much sense to you at first sight but it will be impossible to reason about the functioning of your code once you introduce those other functions that do the same thing but have their names swapped.

Instead of “overloading” the names used by the standard library, use new names that convey precisely what you mean. In your case, you're not really interested in a “minimum”. Rather, you want to cap a value. Mathematically, this is the same operation but semantically, it is not quite. So why not just a function

int cap(int value, int limit) { return (value > limit) ? limit : value; }

that does what is needed and tells so from its name. (You could also implement capin terms of min as shown in timster's answer).

Another frequently used function name is clamp. It takes three arguments and “clamps” a provided value into the interval defined by the other two values.

int clamp(int value, int lower, int upper) {
    assert(lower <= upper);  // precondition check
    if (value < lower) return lower;
    else if (value > upper) return upper;
    else return value;
}

If you're using such a generally known function name, any new person joining your team (including the future you coming back to the code after a while) will quickly understand what is going on instead of cursing you for having confused them by breaking their expectations about function names they thought they knew.

4
  • 2
    You could also name it getValueNotBiggerThan(x, limit) this is the right approach, and with a decent compiler the function will be inlined and the resulting machine code will be exactly the same as using built-ins
    – Falco
    Commented Jun 8, 2016 at 20:31
  • 20
    @Falco I would say that “cap” is almost a synonym for “get value not bigger than” but I'm not going to paint that bike shed today. ;-)
    – 5gon12eder
    Commented Jun 8, 2016 at 21:24
  • 1
    clamp is used extensively in all kinds of signal processing and similar operations (image processing, etc), so that's definitely what I'd be going with too. Although I wouldn't say it requires upper and lower bounds: I've seen it quite often for only one direction as well.
    – Voo
    Commented Jun 10, 2016 at 20:18
  • 1
    Was gonna mention clamp, too. And if it's written correctly, you can just use a bound of infinity/negative infinity for when you only want it bound on one way. Eg, to ensure that a number is not greater than 255 (but no lower bound), you'd use clamp(myNumber, -Infinity, 255).
    – Kat
    Commented Jun 14, 2016 at 20:20
113

If you make a function like that where minimize(4, 10) returns 10, then I'd say that is inadvisable because your fellow programmers may strangle you.

(Okay, maybe they will not literally strangle you to death, but seriously... Don't do that.)

3
  • 2
    Also entirely possible is that when you come back to work on this code in a few years, you yourself will spend several hours trying to figure out why your figures are wrong when you call minimise...
    – Paddy
    Commented Jun 10, 2016 at 14:46
  • Aww... this answer used to have a really cool (and pretty highly voted) comment. (It was also the first comment on the answer, which helped the humorous flow.)
    – TOOGAM
    Commented Jun 11, 2016 at 22:12
  • I keep wanting to get punch cards and cross out the DO NOT (out of "DO NOT Spindle, Fold, or Mutilate"). Somebody who implemented something like this would receive a card. Commented Jun 12, 2016 at 0:37
26

Aliasing a function is fine, but don't try to change the meaning of existing terms

It's OK to create an alias of the function - common libraries do that all the time.

However, it's a bad idea to use terms in a way contrary to common usage, like your example where to your mind max and min should be flipped. It's confusing to other programmers, and you will be doing yourself a disservice by training yourself to keep interpreting these terms in a non-standard way.

So in your case, abandon the "mininum/maximum" parlance that you find confusing and create your own, easy to understand code.

Refactoring your example:

int apply_upper_bound(int x, int y)
{
    return min(x, y);
}


int apply_lower_bound(int x, int y)
{
    return max(x, y)
}

As an added bonus, every time you look at this code, you will be reminding yourself how min and max are used in your programming language. Eventually, it will come to make sense in your head.

8
  • 4
    I think you've misunderstood the application of min and max which confuses the OP. It's when min is used to set a fixed upper boundary on some value. get_lower_value would be just as counterintuitive in this application. If I were to choose an alternative name for this operation, I'd call it supremum, though I'm not sure how many programmers would immediately understand that. Commented Jun 8, 2016 at 23:39
  • 3
    @leftaroundabout: the supremum of the set {1, 2} is 2, so do not use the name supremum for the function get_lower_value that's defined above to just call min. It causes the next programmer exactly the same problem as calling it maximise. I'd suggest calling it apply_upper_bound, but I'm not sure that's perfect. It's still odd because it works the same whichever way around you put the parameters, but the name implies that one of the parameters is "the value" and the other is "the bound", and that they're somehow different. Commented Jun 9, 2016 at 8:19
  • 1
    I think you're essentially relying on the reader not to be too familiar with the word supremum, so that they take your meaning rather than the English meaning. It's fine to define jargon local to your code, but kind of the point of the question is what do do when the jargon you want directly contradicts an already-familiar meaning, and I'm afraid I think you're still doing that (for some version of "English" relevant only to those who've studied STEM subjects) Commented Jun 9, 2016 at 9:11
  • 3
    This policy "Aliasing a function is fine, but don't try to change the meaning of existing terms" is perfect and beautifully expressed. Secondly, the idea that you should not rename in a way which is confusing in relation to built-in functions (and: even if the built in functions are poorly named / stupid / confusing) is a perfect idea. Re the max/min example which came up on this page, it's total confusion heh.
    – Fattie
    Commented Jun 9, 2016 at 17:44
  • 1
    OK, I edited the answer to use "apply_upper_bound" which both fits the OP's reasoning and avoids overloading terminology. Verbosity is not a problem. I'm not setting out to write the perfect method name here, just set some ground rules for naming aliases. The OP can edit it to whatever he finds most clear and concise.
    – Tim Grant
    Commented Jun 10, 2016 at 1:24
12

I love this question. Let's break it down though.

1: Should you wrap a single line of code?

Yes, I can think of plenty of examples where you might do this. Maybe you are enforcing typed parameters or hiding a concrete implementation behind an interface. In your example you are essentially hiding a static method call.

Additionally you can do a lot of stuff in a single line these days.

2: Are the names 'Min' and 'Max' confusing

Yes! They totally are! A clean coding guru would rename them "FunctionWhichReturnsTheLargestOfItsParameters" or something. Fortunately we have documentation and (if you are lucky) IntelliSense and comments to help us out so anyone who is confused by the names can read up on what they are supposed to do.

3: Should you rename them to something else yourself.

Yup, go for it. For instance, you could have:

class Employee
{
    int NumberOfHolidayDaysIShouldHave(int daysInLue, int maxAllowableHolidayDays)
    {
         // Return the number of days in lue, but keep the value under the max allowable holiday days!
         // Don't use max, you fool!!
         return Math.Max(daysInLue, maxAllowableHolidayDays)
    }
}

It adds meaning, and the caller doesn't have to or want to know how to calculate the value.

4: Should you rename "min" to "maximize"

No!! are you crazy?! But yes, the question underlines the point that different people read different meanings into function and object names. What one person finds clear and conventional another finds opaque and confusing. That is why we have comments. You should instead write:

// Add x and y, but don't let it go over 255
s = min(x + y, 255);

Then when someone reads

// Add x and y, but don't let it go over 255
s = max(x + y, 255);

they know you made a mistake.

11
  • 6
    Funny! In your example you have made the exact error the op is concerned with: you want the number of holidays to be no bigger than maxHolidaysAllowed, so you nee min(a,b)
    – Falco
    Commented Jun 8, 2016 at 20:28
  • 14
    If clean code truly suggests that ludicrous names such as FunctionWhichReturnsTheLargestOfItsParameters are a good thing, I want no part of it. Commented Jun 8, 2016 at 20:45
  • 1
    @Falco lol oops!!!
    – Ewan
    Commented Jun 8, 2016 at 20:49
  • 4
    @DavidHammen: it doesn't really, since no real programming style puts the wart FunctionWhichReturns on the front of every function (that doesn't throw an exception or terminate). You could end up with getMinimum, getLarger (or getLargest with more than 2 inputs) though, by following real advice along the lines that (a) pure functions and/or "getters" should use the wart get, (b) English words should not be abbreviated in names. Clearly that's too verbose for those who decide to call such functions max. Commented Jun 9, 2016 at 8:27
  • 1
    yeah, see @falco's comment. i couldn't really correct it after that!
    – Ewan
    Commented Jun 9, 2016 at 14:06
4

No. Dont make functions with names very similar to built-in functions, but which actually does the opposite. It may seem intuitive to you, but is going to be very confusing to other developers, and even to yourself some time in the future when you have more experience.

The meaning of max is "the maximum of", but your "intuitive" understanding is something like "to the maximum of". But this is simply a wrong understanding of the function, and changing the name from max to maximum does not communicate your different interpretation. Even if you strongly believe the language designers made a mistake, don't do something like this.

But changing the name to say cap(x, limit) as has been suggested would be fine, since it clearly does communicate the intention, even if it just wraps min.

3

What may be confusing you is either using Capped in your function name or your understanding of what placing a cap means. It is a limiter and doesn't require a max of anything.

If you're asked for the lowest, smallest or earliest, do you feel like Max is the appropriate function?

Leave min and max alone. Write tests so at least you'll get it correct the second time.

If you're required to use these functions so much in your project, you will come up with some sort of hint to help you clarify which one to use. Sort of like < or >, the wide part of the mouth faces the larger value.

1
  • 1
    +1 for possible terminology confusion as an underlying problem. I think the confusion starts at the comment "return the sum, with the maximum of 255", so he thinks the max function is more appropriate, but logic-wise min is the thing he's actually looking for.
    – Revenant
    Commented Jun 9, 2016 at 3:57
2

To answer your question: Is there any other reason this would be inadvisable? What about in a group project? It makes sense that you want your own functions, which is no problem. Just make sure they are in your own helper class and are not easily callable for others unless they import it. (Joes.Utilities.)

But to look again at your problem, basically I would instead be thinking:

return (input >= 255) ? 255 : input;

You're getting confused because you're trying to apply your brain logic to these min/max functions. Instead just speak in English. if the input is greater than or equal to 255 then return 255 otherwise return the input.

Which is:

if (input >= 255) {
   255 
} else {
   input
}

My opinion. You're going for the max\min functions for the wrong reasons, the speed of these things is negligible. Do what makes sense.

1
  • 1
    One of my senior coworkers always used to say "Let the computer do the thinking for you".
    – user81965
    Commented Jun 9, 2016 at 17:05
1

While I understand your problem, I'd be reluctant to do this. It would be better to simply drill into your skull what min() and max() do.

Most programmers know what min() and max() functions do -- even if, like you, they sometimes struggle with their intuition over which to use at any given time. If I'm reading a program and see max(x,y), I immediately know what it does. If you create your own "alias" function, then anyone else reading your code will not know what this alias does. They have to find your function. It unnecessarily breaks the flow of reading and forces the reader to do some extra thinking to understand your program.

If you have trouble figuring out which to use at some point, I'd say, add a comment explaining it. Then if a future reader is similarly confused, your comment should clear it up. Or if you do it wrong but the comment explains what you were trying to do, the person trying to debug it will have a clue.

Once you alias a function because the name clashes with your intuition ... is this the only case where that's a problem? Or are you going to alias other functions? Maybe you're confused by "read" and find it easier to think of it as "accept", you change "append" to "StringTogether", "round" to "DropDecimals", etc, etc. Take this to a ridiculous extreme and your programs will be incomprehensible.

Indeed, years ago I worked with a programmer who didn't like all the punctuation in C. So he wrote a bunch of macros to let him write "THEN" instead of "{" and "END-IF" instead of "}" and dozens of other such substitutions. So then when you tried to read his programs, it didn't even look like C any more, it was like having to learn a whole new language. I don't remember now whether "AND" translated to "&" or "&&" -- and that's the point. You undermine the investment people have made in learning the language and the library.

That said, I would not say that a function that does nothing but call a standard library function is necessarily bad. If the point of your function is not to create an alias, but to encapsulate behavior that just happens to be a single function, this could be good and proper. I mean, if logically and inevitably you have to do a max at this point in the program, then just call max directly. But if you have to perform some calculation that today calls for a max, but that might be modified in the future to do something else, then an intermediate function is appropriate.

0

It is OK to rename built in functions provided the new names makes your code a lot clear and will not be miss understood by anyone. (If you are using C/C++ do not use a #define as it makes it hard to see what is going on.) The name of a function should act like a comment explaining what the calling code is doing and why it is doing to.

You are not the only person that has had this problem with min and max, however I yet to see a good general solution that works across all domains. I think one problem with naming of these functions is that the two arguments have different logical meanings, but are presented as meaning the same.

If your language allows it you could try

return  calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)

return  CDsInStocked.ButNoMoreThen(CasesInStock)
0

No.

You don't write your wrappers. Names of those wrappers are not very significant.

What you're trying to do is a kind code obfuscation. You're inventing an extra layer which serves 2 purposes:

  1. Other people can't understand your code.
  2. You never learn to understand other people's code.

By hiding away things you're not comfortable with, you're only hurting your code now and yourself in the future. You can't grow by staying in your comfort zone. What you need is to learn how min and max work.

-1

It is Ok, and not really counterintuitive to use Min,Max to rein in over and undershoot. This is also done using :

  • floor() and ceil()
  • clamp,limit,bounds
  • and of course mod with high order truncation.

In firmware it dates back further than MMX, which itself predates modern 3D graphics which relies on this extensivley.

Replacing an industry standard function even locally would worry me, a derivative name may be better. C++ students might overload for their obscure class perhaps.

2
  • I'm sorry, what's with MMX? Commented Jun 11, 2016 at 8:21
  • I was thinking of "saturated values", where the result is capped at a limit. The idea was code could be simpler if it did not have to include boundary and overflow tests (in graphics to avoid cycling or going positive by travelling past limits of negative). PSUBSB/PSUBSW. I concede the mechanism may not be the same, but the effect and the intent of the function is.
    – mckenzm
    Commented Jun 12, 2016 at 17:11
-1

It's fine in some cases, but not in your example, because there are much better ways to word it:
saturate, clamp, clip, etc.

-1

I'd rather create a generic function named 'bounded'

//Assumes lower_bound <= upper_bound
template <typename T>
T bounded(T value, T lower_bound, T upper_bound){
    if (value < lower_bound)
        return lower_bound;
    if (value > upper_bound)
        return upper_bound;
    return value;
}

//Checks an upper (by default) or lower bound
template <typename T>
T bounded(T value, T bound, bool is_upper_bound = true){
    if (is_upper_bound){
        if (value > bound)
            return bound;
    }
    else {
        if (value < bound)
            return bound;
    }
    return value;
}

or with the usage of 'min' and 'max'

//Assumes lower_bound <= upper_bound
template <typename T>
T bounded(T value, T lower_bound, T upper_bound){
    return max(min(value, upper_bound), lower_bound);
}

//Checks an upper (by default) or lower bound
template <typename T>
T bounded(T value, T bound, bool is_upper_bound = true){
    if (is_upper_bound)
        return min(value, bound);
    else
        return max(value, bound);
}
-1

What about calling your functions:

atmost(x,255): return the lower of x or 255 at most.

atleast(10,x): return the higher of x or at least 10.

-1

min(x+y, MAX_VALUE); would carry much more meaning than myCustomFunction(x, y);

So the answer is YES, it is unadvisable. It serves only as an alias to your brain language.

Not the answer you're looking for? Browse other questions tagged or ask your own question.