Est-il déconseillé de créer une fonction qui renomme essentiellement une fonction intégrée?

40

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 pas de 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'essaie de fixer un maximum ou un minimum, je le récupère à l'envers.

//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, utiliser les fonctions intégrées sera plus rapide, mais cela me semble être une microoptimisation inutile. Y a-t-il une autre raison pour laquelle cela serait déconseillé? Qu'en est-il dans un projet de groupe?

Devsman
la source
72
Si min et max nuisent à votre lisibilité, pensez à échanger puis à des "si" réguliers. Parfois, cela vaut la peine d'écrire un peu plus de code pour avoir une meilleure lisibilité.
T. Sar - Réintégrer Monica le
23
avec un court post, vous avez détruit le concept de "codage propre". Je vous salue monsieur!
Ewan
21
Peut-être que vous pouvez envisager le C ++ 11 std::clampfonction ou quelque chose de similaire.
rwong
15
Peut-être que de meilleurs noms seraient up_to(pour min) et at_least(pour max)? Je pense qu'ils expriment mieux la signification que minimizeetc., bien qu'il faille peut-être un instant pour comprendre pourquoi ils sont commutatifs.
Warbo
59
minet maxet aussi minimizeet maximizesont des noms totalement faux pour les fonctions que vous voulez écrire. Le défaut minet maxfont beaucoup plus de sens. Vous avez en réalité presque les bons noms de fonctions. Cette opération s'appelle serrage ou recouvrement et vous avez écrit deux fonctions de recouvrement. Je suggère capUpperBoundet capLowBound. Je n'ai pas à expliquer à qui que ce soit qui fait quoi, c'est évident.
Slebetman

Réponses:

120

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 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 pour vous au premier abord, 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 leurs noms ont été échangés.

Au lieu de «surcharger» les noms utilisés par la bibliothèque standard, utilisez de nouveaux noms qui traduisent précisément ce que vous voulez dire. Dans votre cas, un «minimum» ne vous intéresse pas vraiment. Au contraire, vous voulez plafonner une valeur. Mathématiquement, il s’agit de 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; }

cela fait ce qui est nécessaire et le dit de son nom. (Vous pouvez également mettre capen œuvre ce minqui est indiqué dans la réponse de timster ).

Un autre nom de fonction fréquemment utilisé est clamp. Il prend trois arguments et "bloque" 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 reviendrez au code après un certain temps) comprendra rapidement ce qui se passe au lieu de vous maudire de les avoir confondues en les brisant. attentes concernant les noms de fonction qu’ils pensaient connaître.

5gon12eder
la source
2
Vous pouvez également le nommer getValueNotBiggerThan (x, limit). C’est la bonne approche. Avec un compilateur décent, la fonction sera alignée et le code machine résultant sera exactement le même que celui qui est utilisé
Falco
20
@ Falco Je dirais que «cap» est presque synonyme de «gagnez pas plus que de valeur», mais je ne vais pas peindre ce garage à vélos aujourd'hui. ;-)
5gon12eder
1
clampest largement utilisé dans tous les types de traitement du signal et d’opérations similaires (traitement d’image, etc.), c’est donc certainement ce que je voudrais aussi. Bien que je ne dirais pas que cela nécessite des limites supérieure et inférieure: je l’ai souvent vu dans une seule direction également.
Voo
1
J'allais en parler clampaussi. Et si c'est écrit correctement, vous pouvez simplement utiliser une borne d'infini / infini négatif lorsque vous ne voulez que le lien lié dans un sens. Par exemple, pour vous assurer qu'un nombre n'est pas supérieur à 255 (mais pas de limite inférieure), vous utiliseriez clamp(myNumber, -Infinity, 255).
Kat
115

Si vous créez une fonction comme celle-ci, où minimize(4, 10)renvoie 10 , alors je dirais que c'est déconseillé, car vos collègues programmeurs risquent de vous étrangler.

(D'accord, peut-être qu'ils ne vous étrangleront pas littéralement à mort, mais sérieusement ... Ne faites pas ça.)

Telastyn
la source
2
Il est également tout à fait possible que lorsque vous reviendrez travailler sur ce code dans quelques années, vous passerez vous-même plusieurs heures à essayer de comprendre pourquoi vos chiffres sont incorrects lorsque vous appelez minimiser ...
Paddy
Aww ... cette réponse avait un commentaire vraiment cool (et assez fortement voté). (C'était aussi le premier commentaire sur la réponse, ce qui a contribué à la fluidité de l'humour.)
TOOGAM
Je continue à vouloir obtenir des cartes perforées et rayer DO NOT(sur "NE PAS broyer, plier ou mutiler"). Quelqu'un qui implémenterait quelque chose comme ça recevrait une carte.
Clockwork-Muse
26

Il est correct d'aliaser une fonction, mais n'essayez pas de changer le sens des termes existants

C'est bien de créer un alias de la fonction - les bibliothèques communes le font tout le temps .

Cependant, il est déconseillé d’utiliser des termes d’une manière contraire à l’usage courant, comme dans votre exemple où inverser max et min devrait être dans votre esprit. C'est déroutant pour les autres programmeurs, et vous vous rendrez un mauvais service en vous entraînant à continuer à interpréter ces termes de 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 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 regarderez ce code, vous vous rappellerez comment min et max sont utilisés dans votre langage de programmation. Cela finira par prendre un sens dans votre tête.

Tim Grant
la source
4
Je pense que vous avez mal compris l'application minet maxqui confond le PO. C'est quand minest utilisé pour définir une limite supérieure fixe sur une valeur. get_lower_valueserait tout aussi contre-intuitif dans cette application. Si je devais choisir un autre nom pour cette opération, je l'appellerais supremum , bien que je ne sache pas combien de programmeurs le comprendraient immédiatement.
gauche autour du
3
@leftaroundabout: le nombre suprême de l'ensemble {1, 2} est 2, aussi n'utilisez pas le nom supremumde la fonction get_lower_valuedéfinie ci-dessus pour simplement appeler min. Cela pose le même problème au programmeur suivant que l’appeler maximise. Je suggérerais de l'appeler apply_upper_bound, mais je ne suis pas sûr que ce soit parfait. Cela reste étrange car cela fonctionne de la même manière, quel que soit le paramètre que vous définissez, mais le nom implique que l’un des paramètres est "la valeur" et que l’autre est "la borne" et qu’ils sont en quelque sorte différents.
Steve Jessop
1
Je pense que vous comptez essentiellement sur le lecteur pour qu'il ne connaisse pas trop le mot supremum, afin qu'il prenne votre sens plutôt que le sens anglais. C'est bien de définir le jargon local dans votre code, mais la question est de savoir ce qu'il faut faire quand le jargon que vous voulez contredit directement un sens déjà familier, et j'ai bien peur que vous ne le fassiez pas une version de "l'anglais" ne concerne que ceux qui ont étudié le sujet des STEM)
Steve Jessop
3
Cette politique "aliaser une fonction, c'est bien, mais n'essayez pas de changer le sens des termes existants" est parfaite et magnifiquement exprimée. Deuxièmement, l’idée que vous ne devriez pas renommer d’une manière qui prête à confusion par rapport aux fonctions intégrées (et: même si les fonctions intégrées sont mal nommées / stupide / confuse) est une idée parfaite. Concernant l' exemple max / min qui est apparu sur cette page, c'est une confusion totale heh.
Fattie
1
OK, j’ai édité la réponse pour utiliser "apply_upper_bound", ce qui correspond au raisonnement du PO et évite de surcharger la terminologie. La verbosité n'est pas un problème. Je ne veux pas écrire le nom de méthode parfait ici, mais simplement définir quelques règles de base pour nommer des alias. Le PO peut éditer ce qu'il trouve le plus clair et le plus concis possible.
Tim Grant
12

J'aime cette question. Décomposons cependant.

1: Faut-il envelopper une seule ligne de code?

Oui, je peux penser à de nombreux exemples où vous pourriez faire cela. Peut-être appliquez-vous des paramètres typés ou cachez-vous 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 en une seule ligne ces jours-ci.

2: Les noms 'Min' et 'Max' sont-ils déroutants?

Oui! Ils sont totalement! Un gourou du code propre les renommerait "FunctionWhichReturnsTheLargestOfItsParameters" ou quelque chose du genre. Heureusement, nous avons la documentation et (si vous êtes chanceux) IntelliSense et des commentaires pour nous aider afin que toute personne déroutée par les noms puisse lire ce qu’ils sont censés faire.

3: Devriez-vous les renommer vous-même?

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 à savoir ou veut 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 dans les noms de fonctions et d'objets. Ce qu'une personne trouve clair et conventionnel, une autre 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 commis une erreur.

Ewan
la source
6
Drôle! Dans votre exemple, vous avez commis l'erreur exacte qui préoccupe l'opérateur: vous voulez que le nombre de vacances ne soit pas supérieur à maxHolidaysAllowed, vous devez donc min (a, b)
Falco
14
Si un code vierge suggère vraiment que des noms ridicules tels que FunctionWhichReturnsTheLargestOfItsParameterssont une bonne chose, je ne veux pas en faire partie.
David Hammen
1
@ Falco lol oops !!!
Ewan
4
@ David Hammen: ce n'est pas vraiment le cas, car aucun style de programmation réel ne place la verrue FunctionWhichReturnsau premier plan de chaque fonction (cela ne déclenche pas d'exception ni ne se termine). Vous pourriez finir avec getMinimum, getLarger(ou getLargestavec plus de 2 entrées) si, en suivant des conseils concrets sur les lignes qui (a) des fonctions pures et / ou « apporteurs » doivent utiliser la verrue get, (b) les mots anglais ne doivent pas être abrégé noms. Clairement, c'est trop verbeux pour ceux qui décident d'appeler de telles fonctions max.
Steve Jessop
1
oui, voir le commentaire de @ falco. Je ne pouvais pas vraiment le corriger après ça!
Ewan
4

Non . Ne faites pas de fonctions avec des noms très similaires aux fonctions intégrées, mais qui fait le contraire . Cela peut sembler intuitif pour vous, 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 maxest "le maximum de", mais votre compréhension "intuitive" est quelque chose comme "au maximum de". Mais ceci est simplement une mauvaise compréhension de la fonction, et changer le nom de maxen maximumne communique pas votre interprétation différente. Même si vous croyez fermement que les concepteurs de langage ont commis une erreur, ne faites pas quelque chose comme ça.

Mais changer le nom pour dire, cap(x, limit)comme cela a été suggéré, conviendrait, puisqu'il indique clairement l'intention, même si cela ne fait que conclure min.

JacquesB
la source
3

Ce qui peut vous dérouter, c'est soit d'utiliser Capped dans le nom de votre fonction, soit de comprendre ce que signifie placer un cap. C'est un limiteur et ne nécessite pas un maximum de rien.

Si on vous demande le plus petit, le plus petit ou le plus ancien, avez-vous le sentiment que Max est la fonction appropriée?

Laissez min et max seuls. Ecrivez des tests pour que vous puissiez au moins le corriger une deuxième fois.

Si vous devez autant utiliser ces fonctions dans votre projet, vous obtiendrez un indice qui vous aidera à préciser laquelle utiliser. Un peu comme <ou>, la partie large de la bouche fait face à la plus grande valeur.

JeffO
la source
1
+1 en cas de confusion terminologique possible en tant que problème sous-jacent. Je pense que la confusion commence au commentaire "retourne la somme, avec le maximum de 255", il pense donc que la maxfonction est plus appropriée, mais la logique minest ce qu'il recherche réellement.
Revenant
2

Pour répondre à votre question: existe-t-il une autre raison pour laquelle cela serait déconseillé? Qu'en est-il dans un projet de groupe? Il est logique que vous vouliez vos propres fonctions, ce qui ne pose aucun problème. Assurez-vous simplement qu'ils sont dans votre propre classe d'assistance et qu'ils ne sont pas facilement appelables pour les autres, à moins qu'ils ne l'importent. (Joes.Utilitaires.)

Mais pour revoir 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. Au lieu de cela, parlez simplement en anglais. ifle inputest greater than or equal to 255 then return 255autrement returnle input.

Lequel est:

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

Mon avis. Vous utilisez les fonctions max \ min pour les mauvaises raisons, la vitesse de ces opérations est négligeable. Faites ce qui a du sens.

Digne7
la source
1
Un de mes collègues chevronnés disait toujours "Laissez l'ordinateur penser à votre place".
1

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 ont parfois du mal à utiliser leur intuition à un moment donné. 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", toute personne lisant votre code ne saura pas ce que fait cet alias. Ils doivent trouver votre fonction. Cela interrompt inutilement la lecture et oblige le lecteur à réfléchir plus avant pour comprendre votre programme.

Si vous avez du mal à déterminer laquelle utiliser à un moment donné, ajoutez un commentaire pour l'expliquer. Ensuite, si un futur lecteur est aussi confus, votre commentaire devrait l’éclaircir. Ou si vous le faites mal mais que le commentaire explique ce que vous tentiez de faire, la personne qui tente de le déboguer aura un indice.

Une fois que vous appelez une fonction parce que le nom se heurte à votre intuition ... est-ce le seul cas où cela pose un problème? Ou allez-vous alias d'autres fonctions? Peut-être que vous êtes dérouté par "lire" et que vous trouvez plus facile de le considérer comme "accepter", vous changez "append" en "StringTogether", "round" en "DropDecimals", etc., etc. Portez ceci à une extrême ridicule. 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 lui permettre d'écrire "THEN" au lieu de "{" et "END-IF" au lieu de "}" et des dizaines d'autres substitutions de ce type. Ainsi, lorsque vous avez essayé de lire ses programmes, cela ne ressemblait même plus à C, c'était comme si vous deviez apprendre une nouvelle langue. Je ne me souviens pas maintenant si "AND" a été traduit par "&" ou "&&" - et c'est le but. Vous sapez l'investissement que les gens ont fait pour apprendre la langue et la bibliothèque.

Cela dit, je ne dirais pas qu'une fonction qui ne fait rien mais appelle 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 pourrait être bon et correct. Je veux dire, si logiquement et inévitablement vous devez faire un max à ce stade du programme, alors appelez directement max. Mais si vous devez effectuer des calculs qui exigent aujourd'hui un maximum, mais qui pourraient être modifiés à l'avenir pour faire autre chose, une fonction intermédiaire est appropriée.

Geai
la source
0

Il est possible de renommer des fonctions intégrées à condition que les nouveaux noms rendent votre code très clair et ne soient pas compris par personne. (Si vous utilisez C / C ++, n'utilisez pas #define car il est difficile de voir ce qui se passe.) Le nom d'une fonction doit agir comme un commentaire expliquant ce que fait le code d'appel et pourquoi il le fait. .

Vous n'êtes pas la seule personne à avoir eu ce problème avec min et max, mais je ne vois pas encore de 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)
Ian
la source
0

Non.

Vous n'écrivez pas vos enveloppes. Les noms de ces enveloppes ne sont pas très significatifs.

Ce que vous essayez de faire, c'est une obfuscation de code aimable. Vous inventez une couche supplémentaire qui sert 2 objectifs:

  1. Les autres personnes ne peuvent pas comprendre votre code.
  2. Vous n'apprenez jamais à comprendre le code des autres.

En cachant des choses avec lesquelles vous n'êtes pas à l'aise, vous ne faites que nuire à 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 à travailler minet à maxtravailler.

Agent_L
la source
-1

C’est bien, et ce n’est pas vraiment contre-intuitif d’utiliser Min, Max pour céder le pas à la vitesse supérieure. Ceci est également fait en utilisant:

  • étage () et ceil ()
  • pince, limite, bornes
  • et bien sûr mod avec une troncature d'ordre élevé.

Dans le firmware, il remonte plus loin que MMX, qui est antérieur aux graphiques 3D modernes qui reposent sur cette extension.

Remplacer une fonction standard de l'industrie même localement m'inquiéterait, un nom dérivé peut être préférable. Les étudiants C ++ pourraient peut-être surcharger leur obscure classe.

Mckenzm
la source
Je suis désolé, c'est quoi avec MMX?
Tobia Tesan
Je pensais aux "valeurs saturées", où le résultat est limité. L’idée était que le code pourrait être plus simple s’il n’avait pas à inclure de tests de limite et de dépassement de capacité (dans les graphiques pour éviter le cyclisme ou devenir positif en dépassant les limites du négatif). PSUBSB / PSUBSW. J'admets que le mécanisme n'est peut-être pas le même, mais l'effet et l'intention de la fonction sont.
mckenzm
-1

Il est très bien dans certains cas, mais pas dans votre exemple, parce qu'il ya de bien meilleures façons le libeller:
saturate, clamp, clip, etc.

Mehrdad
la source
-1

Je créerais plutôt une fonction générique nommée '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;
}

ou avec l'utilisation de 'min' et '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);
}
David Ferrer
la source
-1

Qu'en est-il d'appeler vos fonctions:

atmost(x,255): retourne le plus bas de x ou 255 au plus.

atleast(10,x): retourne le plus haut de x ou au moins 10.

Jonc
la source
-1

min(x+y, MAX_VALUE); porterait beaucoup plus de sens que myCustomFunction(x, y);

Donc, la réponse est OUI, c'est déconseillé . Il ne sert que d'alias à votre langage cérébral.

Étincelle
la source