Je suis confus sur les fonctions min et max, dans certains contextes.
Dans un contexte, lorsque vous utilisez les fonctions pour prendre la plus ou moins grande des deux valeurs, il n'y a aucun problème. Par exemple,
//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);
}
Facile. Mais dans un autre contexte, je suis confus. Si j'essaye de fixer un maximum ou un minimum, je l'obtiens en arrière.
//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
}
Est-il déconseillé de créer mes propres fonctions comme suit?
//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)
}
Évidemment, l'utilisation des builtins sera plus rapide mais cela me semble une microoptimisation inutile. Y a-t-il une autre raison pour laquelle cela serait déconseillé? Et dans un projet de groupe?
Comme d'autres l'ont déjà mentionné: ne créez pas une fonction avec un nom similaire à celui d'une fonction intégrée, d'une bibliothèque standard ou d'une fonction généralement utilisée mais modifiez son comportement. Il est possible de s'habituer à une convention de nommage même si cela n'a pas beaucoup de sens à première vue, mais il sera impossible de raisonner sur le fonctionnement de votre code une fois que vous aurez introduit les autres fonctions qui font la même chose mais qui ont leurs noms ont troqué.
Au lieu de "surcharger" les noms utilisés par la bibliothèque standard, utilisez de nouveaux noms qui expriment précisément ce que vous voulez dire. Dans votre cas, vous n'êtes pas vraiment intéressé par un "minimum". Vous souhaitez plutôt plafonner une valeur. Mathématiquement, c'est la même opération mais sémantiquement, ce n'est pas tout à fait. Alors pourquoi pas juste une fonction
int cap(int value, int limit) { return (value > limit) ? limit : value; }
qui fait ce qui est nécessaire et le dit à partir de son nom. (Vous pouvez également implémenter cap
en termes de min
comme indiqué dans timster 's answer ).
Un autre nom de fonction fréquemment utilisé est clamp
. Il prend trois arguments et "serre" une valeur fournie dans l'intervalle défini par les deux autres valeurs.
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;
}
Si vous utilisez un nom de fonction aussi connu, toute nouvelle personne rejoignant votre équipe (y compris l'avenir, vous revenez au code après un certain temps) comprendra rapidement ce qui se passe au lieu de vous maudire de les avoir confondus en brisant leur les attentes concernant les noms de fonction qu'ils pensaient connaître.
Si vous créez une fonction comme celle où minimize(4, 10)
renvoie 1, alors je dirais que c'est déconseillé car vos collègues programmeurs peuvent vous étrangler.
(D'accord, peut-être qu'ils ne vous étrangleront pas littéralement à mort, mais sérieusement ... Ne faites pas ça.)
Aliaser une fonction est très bien, mais n'essayez pas de changer la signification des termes existants
C'est OK de créer un alias de la fonction - bibliothèques communes faites ça tout le temps .
Cependant, c'est une mauvaise idée d'utiliser des termes d'une manière contraire à l'usage courant, comme votre exemple où, à votre avis, max et min devraient être inversés. C'est déroutant pour les autres programmeurs, et vous vous rendrez un mauvais service en vous entraînant à continuer à interpréter ces termes d'une manière non standard.
Donc, dans votre cas, abandonnez le langage "mininum/maximum" que vous trouvez déroutant et créez votre propre code, facile à comprendre.
Refactoring de votre exemple:
int apply_upper_bound(int x, int y)
{
return min(x, y);
}
int apply_lower_bound(int x, int y)
{
return max(x, y)
}
En prime, chaque fois que vous regardez ce code, vous vous rappellerez comment min et max sont utilisés dans votre langage de programmation. Finalement, cela deviendra logique dans votre tête.
J'adore cette question. Décomposons-le cependant.
1: Devriez-vous envelopper une seule ligne de code?
Oui, je peux penser à de nombreux exemples où vous pourriez le faire. Peut-être que vous appliquez des paramètres saisis ou cachez une implémentation concrète derrière une interface. Dans votre exemple, vous masquez essentiellement un appel de méthode statique.
De plus, vous pouvez faire beaucoup de choses sur une seule ligne de nos jours.
2: Les noms "Min" et "Max" prêtent-ils à confusion
Oui! Ils le sont totalement! Un gourou du codage propre les renommerait "FunctionWhichReturnsTheLargestOfItsParameters" ou quelque chose. Heureusement, nous avons de la documentation et (si vous avez de la chance) IntelliSense et des commentaires pour nous aider afin que quiconque est confus par les noms puisse lire ce qu'il est censé faire.
3: Devriez-vous les renommer autrement.
Ouais, allez-y. Par exemple, vous pourriez avoir:
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)
}
}
Cela ajoute du sens et l'appelant n'a pas à ou ne veut pas savoir comment calculer la valeur.
4: Devriez-vous renommer "min" en "maximiser"
Non !! êtes-vous fou?! Mais oui, la question souligne le fait que différentes personnes lisent différentes significations en noms de fonction et d'objet. Ce qu'une personne trouve clair et conventionnel, une autre le trouve opaque et déroutant. C'est pourquoi nous avons des commentaires. Vous devriez plutôt écrire:
// Add x and y, but don't let it go over 255
s = min(x + y, 255);
Puis quand quelqu'un lit
// Add x and y, but don't let it go over 255
s = max(x + y, 255);
ils savent que vous avez fait une erreur.
Non. Ne faites pas de fonctions avec des noms très similaires aux fonctions intégrées, mais qui en fait fait le contraire. Cela peut vous sembler intuitif, mais cela va être très déroutant pour les autres développeurs, et même pour vous-même dans le futur lorsque vous aurez plus d'expérience.
La signification de max
est "le maximum de", mais votre compréhension "intuitive" est quelque chose comme "au maximum de". Mais c'est simplement une mauvaise compréhension de la fonction, et changer le nom de max
en maximum
ne communique pas votre interprétation différente. Même si vous croyez fermement que les concepteurs de langage ont fait une erreur, ne faites pas ça.
Mais changer le nom pour dire cap(x, limit)
comme cela a été suggéré serait bien, car il communique clairement l'intention, même s'il enveloppe simplement min
.
Ce qui peut vous dérouter est d'utiliser Capped dans le nom de votre fonction ou votre compréhension de ce que signifie placer un capuchon. C'est un limiteur et ne nécessite rien de plus.
Si on vous demande le plus bas, le plus petit ou le plus tôt, pensez-vous que Max est la fonction appropriée?
Laissez min et max seuls. Écrivez des tests pour que vous puissiez au moins le corriger la deuxième fois.
Si vous devez tellement utiliser ces fonctions dans votre projet, vous obtiendrez une sorte d'indice pour vous aider à clarifier celle à utiliser. Un peu comme <ou>, la partie large de la bouche fait face à la plus grande valeur.
Pour répondre à votre question: y a-t-il une autre raison pour laquelle cela serait déconseillé? Et dans un projet de groupe? Il est logique que vous souhaitiez vos propres fonctions, ce qui ne pose aucun problème. Assurez-vous simplement qu'ils sont dans votre propre classe d'assistance et ne sont pas facilement appelables pour les autres à moins qu'ils ne l'importent. (Joes.Utilities.)
Mais pour regarder à nouveau votre problème, je penserais plutôt:
return (input >= 255) ? 255 : input;
Vous êtes confus parce que vous essayez d'appliquer votre logique cérébrale à ces fonctions min/max. Parlez simplement en anglais. if
le input
est greater than or equal to 255
then
return 255
sinon return
le input
.
Lequel est:
if (input >= 255) {
255
} else {
input
}
Mon avis. Vous optez pour les fonctions max\min pour de mauvaises raisons, la vitesse de ces choses est négligeable. Faites ce qui a du sens.
Bien que je comprenne votre problème, je serais réticent à le faire. Il serait préférable de simplement percer dans votre crâne ce que font min () et max ().
La plupart des programmeurs savent ce que font les fonctions min () et max () - même si, comme vous, ils éprouvent parfois des difficultés avec leur intuition sur l'utilisation à tout moment. Si je lis un programme et que je vois max (x, y), je sais immédiatement ce qu'il fait. Si vous créez votre propre fonction "alias", quiconque lisant votre code ne saura pas ce que fait cet alias. Ils doivent trouver votre fonction. Cela interrompt inutilement le flux de lecture et oblige le lecteur à réfléchir davantage pour comprendre votre programme.
Si vous avez du mal à déterminer lequel utiliser à un moment donné, je dirais, ajoutez un commentaire l'expliquant. Ensuite, si un futur lecteur est confus de la même manière, votre commentaire devrait l'éclaircir. Ou si vous le faites mal mais que le commentaire explique ce que vous essayez de faire, la personne qui essaie de le déboguer aura un indice.
Une fois que vous alias une fonction parce que le nom entre en conflit avec votre intuition ... est-ce le seul cas où c'est un problème? Ou allez-vous alias d'autres fonctions? Peut-être que vous êtes confondu par "lire" et trouvez plus facile de le considérer comme "accepter", vous changez "ajouter" à "StringTogether", "arrondir" à "DropDecimals", etc., etc. et vos programmes seront incompréhensibles.
En effet, il y a des années, j'ai travaillé avec un programmeur qui n'aimait pas toute la ponctuation en C. Il a donc écrit un tas de macros pour le laisser écrire "ALORS" au lieu de "{" et "FIN-IF" au lieu de "}" et des dizaines d'autres substitutions. Alors quand vous avez essayé de lire ses programmes, ça ne ressemblait même plus à C, c'était comme devoir apprendre un tout nouveau langage. Je ne me souviens pas maintenant si "ET" a été traduit en "&" ou "&&" - et c'est le point. Vous sapez l'investissement que les gens ont fait dans l'apprentissage de la langue et de la bibliothèque.
Cela dit, je ne dirais pas qu'une fonction qui ne fait rien d'autre que d'appeler une fonction de bibliothèque standard est nécessairement mauvaise. Si le but de votre fonction n'est pas de créer un alias, mais d'encapsuler un comportement qui se trouve être une seule fonction, cela peut être bon et approprié. Je veux dire, si logiquement et inévitablement vous devez faire un max à ce stade du programme, alors appelez simplement max directement. Mais si vous devez effectuer un calcul qui demande aujourd'hui un maximum, mais qui pourrait être modifié à l'avenir pour faire autre chose, alors une fonction intermédiaire est appropriée.
Vous n'écrivez pas vos emballages. Les noms de ces enveloppes ne sont pas très significatifs.
Ce que vous essayez de faire est une sorte de brouillage du code. Vous inventez une couche supplémentaire qui sert à 2 fins:
En cachant des choses avec lesquelles vous n'êtes pas à l'aise, vous ne faites que blesser votre code maintenant et vous-même à l'avenir. Vous ne pouvez pas grandir en restant dans votre zone de confort. Ce dont vous avez besoin est d'apprendre comment min
et max
fonctionnent.
Il est correct de renommer les fonctions intégrées à condition que les nouveaux noms rendent votre code beaucoup plus clair et ne seront pas mal compris par personne. (Si vous utilisez C/C++ n'utilisez pas de #define car cela rend difficile de voir ce qui se passe.) Le nom d'une fonction doit agir comme un commentaire expliquant ce que fait le code appelant et pourquoi il le fait pour .
Vous n'êtes pas la seule personne à avoir eu ce problème avec min et max, mais je n'ai pas encore vu une bonne solution générale qui fonctionne dans tous les domaines. Je pense qu'un problème avec la dénomination de ces fonctions est que les deux arguments ont des significations logiques différentes, mais sont présentés comme signifiant la même chose.
Si votre langue le permet, vous pouvez essayer
return calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)
return CDsInStocked.ButNoMoreThen(CasesInStock)