É desaconselhável fazer uma função que essencialmente renomeie uma função interna?

40

Eu me confundo com as funções min e max, em certos contextos.

Em um contexto, quando você está usando as funções para obter o maior ou menor de dois valores, não há problema. Por exemplo,

//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);
}

Fácil. Mas em outro contexto, fico confuso. Se eu estou tentando definir um máximo ou mínimo, eu entendo de trás para frente.

//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
}

É desaconselhável fazer minhas próprias funções da seguinte maneira?

//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)
}

Obviamente, usar o builtins será mais rápido, mas isso parece uma micro-otimização desnecessária para mim. Existe algum outro motivo para isso ser desaconselhável? Que tal em um projeto de grupo?

    
por Devsman 08.06.2016 / 20:35
fonte

15 respostas

120

Como outros já mencionaram: não crie uma função com um nome que seja semelhante ao de uma biblioteca interna, biblioteca padrão ou função geralmente usada, mas mude seu comportamento. É possível se acostumar a uma convenção de nomenclatura mesmo que não faça muito sentido para você à primeira vista, mas será impossível raciocinar sobre o funcionamento do seu código quando você introduzir essas outras funções que fazem a mesma coisa, mas têm seus nomes trocaram.

Em vez de "sobrecarregar" os nomes usados pela biblioteca padrão, use novos nomes que transmitam exatamente o que você quer dizer. No seu caso, você não está realmente interessado em um "mínimo". Em vez disso, você quer limitar um valor. Matematicamente, esta é a mesma operação, mas semanticamente, não é bem assim. Então, porque não apenas uma função

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

que faz o que é necessário e diz de seu nome. (Você também pode implementar cap em termos de min , conforme mostrado em timster 's answer ).

Outro nome de função freqüentemente usado é clamp . São necessários três argumentos e “grampeiam” um valor fornecido no intervalo definido pelos outros dois valores.

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;
}

Se você estiver usando um nome de função tão geralmente conhecido, qualquer nova pessoa se juntando à sua equipe (incluindo o futuro voltando ao código depois de um tempo) entenderá rapidamente o que está acontecendo em vez de xingá-lo por tê-lo confundido quebrando suas expectativas sobre nomes de funções que eles achavam que sabiam.

    
por 08.06.2016 / 22:16
fonte
115

Se você faz uma função como essa em que minimize(4, 10) retorna 10 , então eu diria que isso é desaconselhável porque seus colegas programadores podem estrangulá-lo.

(Ok, talvez eles não vão literalmente te estrangular até a morte, mas seriamente ... não faça isso.)

    
por 08.06.2016 / 20:42
fonte
26

O aliasing de uma função é bom, mas não tente alterar o significado dos termos existentes

Não há problema em criar um alias da função - bibliotecas comuns faz isso o tempo todo .

No entanto, é uma má ideia usar os termos de uma forma contrária ao uso comum, como o exemplo em que, para sua mente, max e min devem ser invertidos. É confuso para outros programadores, e você estará fazendo um desserviço ao treinar a si mesmo para continuar interpretando esses termos de maneira não padronizada.

Portanto, no seu caso, abandone a linguagem "mininum / maximum" que você acha confusa e crie seu próprio código fácil de entender.

Refatorando seu exemplo:

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


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

Como um bônus adicional, toda vez que você olhar para este código, você estará se lembrando de como min e max são usados em sua linguagem de programação. Eventualmente, isso fará sentido em sua cabeça.

    
por 08.06.2016 / 21:46
fonte
12

Eu amo essa pergunta. Vamos dividi-lo embora.

1: Você deve envolver uma única linha de código?

Sim, posso pensar em muitos exemplos em que você pode fazer isso. Talvez você esteja impondo parâmetros digitados ou ocultando uma implementação concreta por trás de uma interface. No seu exemplo, você está essencialmente escondendo uma chamada de método estático.

Além disso, você pode fazer muitas coisas em uma única linha nos dias de hoje.

2: Os nomes 'Min' e 'Max' são confusos

Sim! Eles são totalmente! Um guru de codificação limpo poderia renomeá-los como "FunctionWhichReturnsTheLargestOfItsParameters" ou algo assim. Felizmente, temos documentação e (se você tiver sorte) IntelliSense e comentários para nos ajudar, para que qualquer um fique confuso com os nomes podem ler o que devem fazer.

3: Você deve renomeá-los para outra coisa?

Sim, vá em frente. Por exemplo, você poderia ter:

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)
    }
}

Adiciona significado, e o chamador não precisa nem quer saber como calcular o valor.

4: Você deve renomear "min" para "maximizar"

Não !! você está louco ?! Mas sim, a questão sublinha o fato de que pessoas diferentes lêem diferentes significados em nomes de funções e objetos. O que uma pessoa acha clara e convencional, outra acha opaca e confusa. É por isso que temos comentários. Você deveria escrever:

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

Então, quando alguém lê

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

eles sabem que você cometeu um erro.

    
por 08.06.2016 / 22:02
fonte
4

Não . Não faça funções com nomes muito semelhantes às funções internas, mas que realmente faz o oposto . Pode parecer intuitivo para você, mas vai ser muito confuso para outros desenvolvedores, e até para si mesmo em algum momento no futuro, quando você tiver mais experiência.

O significado de max é "o máximo de", mas o seu entendimento "intuitivo" é algo como "ao máximo de". Mas isso é simplesmente um entendimento errado da função, e alterar o nome de max para maximum não comunica sua interpretação diferente. Mesmo se você acredita firmemente que os projetistas de idiomas cometeram um erro, não faça algo assim.

Mas mudar o nome para dizer cap(x, limit) como foi sugerido seria bom, já que claramente comunica a intenção, mesmo que apenas envolva min .

    
por 09.06.2016 / 16:59
fonte
3

O que pode estar confundindo você é usar o Capped no nome da sua função ou a sua compreensão do que significa colocar um limite. É um limitador e não requer um máximo de nada.

Se lhe for pedido o mais baixo, o menor ou o mais antigo, considera que o Max é a função adequada?

Deixe min e max sozinhos. Escrever testes, pelo menos, você vai acertar na segunda vez.

Se você precisar usar tanto essas funções em seu projeto, você terá uma sugestão para ajudá-lo a esclarecer qual delas usar. Mais ou menos como < ou & gt ;, a parte larga da boca enfrenta o valor maior.

    
por 08.06.2016 / 21:19
fonte
2

Para responder à sua pergunta: Existe algum outro motivo para isso ser desaconselhável? Que tal em um projeto de grupo? Faz sentido que você queira suas próprias funções, o que não é problema. Apenas certifique-se de que eles estejam em sua própria classe auxiliar e não sejam facilmente chamados para outros, a menos que eles os importem. (Joes.Utilities.)

Mas, para olhar novamente para o seu problema, basicamente eu estaria pensando:

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

Você está ficando confuso porque está tentando aplicar sua lógica cerebral a essas funções min / max. Em vez disso, fale apenas em inglês. if o input é greater than or equal to 255 then return 255 senão return o input .

Qual é:

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

Minha opinião. Você está indo para as funções max \ min pelas razões erradas, a velocidade dessas coisas é insignificante. Faça o que fizer sentido.

    
por 09.06.2016 / 07:26
fonte
1

Embora eu entenda seu problema, eu estaria relutante em fazer isso. Seria melhor simplesmente perfurar em seu crânio o que min () e max () fazem.

A maioria dos programadores sabe o que as funções min () e max () fazem - mesmo que, como você, elas às vezes tenham dificuldades com a intuição sobre qual delas usar a qualquer momento. Se estou lendo um programa e vejo max (x, y), imediatamente sei o que ele faz. Se você criar sua própria função "alias", qualquer outra pessoa que ler seu código não saberá o que esse alias faz. Eles precisam encontrar sua função. Ele interrompe desnecessariamente o fluxo de leitura e força o leitor a pensar um pouco mais para entender seu programa.

Se você tiver dificuldade em descobrir qual usar em algum momento, eu diria, adicione um comentário explicando isso. Então, se um leitor futuro for confundido da mesma forma, seu comentário deve esclarecer. Ou se você fizer isso errado, mas o comentário explicar o que você estava tentando fazer, a pessoa que está tentando depurá-lo terá uma pista.

Uma vez que você aliasse uma função porque o nome colide com a sua intuição ... este é o único caso em que isso é um problema? Ou você vai alias outras funções? Talvez você esteja confuso com "read" e ache mais fácil pensar nisso como "accept", você muda "append" para "StringTogether", "round" para "DropDecimals", etc, etc. Leve isso para um extremo ridículo e seus programas serão incompreensíveis.

Na verdade, anos atrás eu trabalhei com um programador que não gostava de toda a pontuação em C. Então ele escreveu um monte de macros para deixá-lo escrever "THEN" ao invés de "{" e "END-IF" ao invés de "}" e dezenas de outras substituições desse tipo. Então, quando você tentou ler seus programas, nem parecia mais o C, era como ter que aprender uma nova língua. Não me lembro agora se "AND" foi traduzido para "&" ou "& &" - e esse é o ponto. Você prejudica o investimento que as pessoas fizeram para aprender a língua e a biblioteca.

Dito isso, não diria que uma função que não faz nada além de chamar uma função de biblioteca padrão é necessariamente ruim. Se o ponto de sua função não é criar um alias, mas encapsular um comportamento que por acaso seja uma única função, isso pode ser bom e adequado. Quero dizer, se logicamente e inevitavelmente você tem que fazer um max neste momento no programa, então basta chamar max diretamente. Mas se você tiver que realizar alguns cálculos que hoje exigem um max, mas que podem ser modificados no futuro para fazer outra coisa, então uma função intermediária é apropriada.

    
por 12.06.2016 / 08:07
fonte
0

Não há problema em renomear as funções incorporadas, desde que os novos nomes tornem seu código muito claro e não sejam ignorados por ninguém. (Se você estiver usando C / C ++ não use um #define, pois isso torna difícil ver o que está acontecendo.) O nome de uma função deve agir como um comentário explicando o que o código de chamada está fazendo e por que está fazendo isso. .

Você não é a única pessoa que teve esse problema com min e max, no entanto, ainda não vi uma boa solução geral que funcione em todos os domínios. Eu acho que um problema com a nomeação dessas funções é que os dois argumentos têm diferentes significados lógicos, mas são apresentados como significando o mesmo.

Se o seu idioma permitir, você pode tentar

return  calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)

return  CDsInStocked.ButNoMoreThen(CasesInStock)
    
por 09.06.2016 / 11:00
fonte
0

Não.

Você não escreve seus wrappers. Os nomes desses invólucros não são muito significativos.

O que você está tentando fazer é uma ofuscação de código. Você está inventando uma camada extra que serve 2 propósitos:

  1. Outras pessoas não entendem seu código.
  2. Você nunca aprende a entender o código de outras pessoas.

Escondendo coisas com as quais você não está confortável, você só estará prejudicando seu código agora e a si mesmo no futuro. Você não pode crescer ficando na sua zona de conforto. O que você precisa é aprender como min e max funcionam.

    
por 10.06.2016 / 14:09
fonte
-1

Está tudo bem, e não é contra-intuitivo usar Min, Max para controlar o over e o undershoot. Isso também é feito usando:

  • floor () e ceil ()
  • clamp, limit, bounds
  • e, claro, mod com truncamento de alta ordem.

No firmware, ela é mais antiga que a MMX, que é anterior aos gráficos 3D modernos que dependem dessa extensividade.

Substituir uma função padrão do setor até mesmo localmente me preocuparia, um nome derivativo pode ser melhor. C ++ estudantes podem sobrecarregar para sua classe obscura, talvez.

    
por 09.06.2016 / 04:04
fonte
-1

Não há problemas em alguns casos, mas não no seu exemplo, porque há maneiras muito melhores de redigir: saturate , clamp , clip , etc.

    
por 09.06.2016 / 05:39
fonte
-1

Eu prefiro criar uma função genérica chamada '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 com o uso de 'min' e '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);
}
    
por 09.06.2016 / 14:58
fonte
-1

Que tal chamar suas funções:

atmost(x,255) : retorna o menor de x ou 255 no máximo.

atleast(10,x) : retorna o maior de x ou pelo menos 10.

    
por 10.06.2016 / 13:10
fonte
-1

min(x+y, MAX_VALUE); teria muito mais significado do que myCustomFunction(x, y);

Então a resposta é SIM, é desaconselhável . Ele serve apenas como um alias para a sua linguagem cerebral.

    
por 09.06.2016 / 03:23
fonte