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

40

Fico confuso 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 o 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 estou tentando definir um máximo ou um mínimo, 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, o uso dos componentes internos será mais rápido, mas isso me parece uma microoptimização desnecessária. Existe alguma outra razão pela qual isso seria desaconselhável? E em um projeto de grupo?

Devsman
fonte
72
Se min e max estão prejudicando sua legibilidade, considere trocar então por "se" s regulares. Às vezes, vale a pena escrever um pouco mais de código para ter melhor legibilidade.
T. Sar - Restabelece Monica
23
com um pequeno post você destruiu todo o conceito de "codificação limpa". Eu te saúdo senhor!
Ewan
21
Talvez você possa considerar a std::clampfunção C ++ 11 ou algo semelhante.
rwong
15
Talvez nomes melhores fossem up_to(para min) e at_least(para max)? Eu acho que eles transmitem o significado melhor do que minimizeetc., embora possa levar um momento para entender por que eles são comutativos.
Warbo 9/06/16
59
minee maxtambém minimizee maximizesão nomes totalmente errados para as funções que você deseja escrever. O padrão mine maxfaz muito mais sentido. Você na verdade QUASE acertou os nomes das funções. Essa operação é chamada de fixação ou nivelamento e você escreveu duas funções de nivelamento. Eu sugiro capUpperBounde capLowBound. Não preciso explicar a ninguém qual faz qual, é óbvio.
slebetman

Respostas:

120

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

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ê deseja limitar um valor. Matematicamente, esta é a mesma operação, mas semanticamente, não é bem. Então, por que não apenas uma função

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

isso faz o que é necessário e diz isso pelo nome. (Você também pode implementar capem termos de mincomo mostrado na timster de resposta ).

Outro nome de função usado com freqüência é clamp. São necessários três argumentos e “prende” 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 geralmente conhecido, qualquer nova pessoa que se juntar à sua equipe (incluindo o futuro em que voltará ao código depois de um tempo) entenderá rapidamente o que está acontecendo, em vez de xingar você por tê-lo confundido, quebrando o expectativas sobre nomes de funções que eles pensavam que sabiam.

5gon12eder
fonte
2
Você também pode nomeá-lo getValueNotBiggerThan (x, limite), este é o caminho certo, e com um compilador decente a função vai ser embutido e o código de máquina resultante será exatamente o mesmo que usar built-ins
Falco
20
@Falco Eu diria que "cap" é quase um sinônimo de "obter valor não maior que", mas não vou pintar o barracão hoje. ;-)
5gon12eder
11
clampé usado extensivamente em todos os tipos de processamento de sinal e operações semelhantes (processamento de imagem etc.), então é definitivamente com isso que eu também iria. Embora eu não diria que isso exige limites superior e inferior: eu já o vi muitas vezes apenas por uma direção.
Voo
11
clampTambém ia mencionar . E se estiver escrito corretamente, você poderá usar apenas um limite de infinito / infinito negativo para quando você quiser apenas um limite. Por exemplo, para garantir que um número não seja maior que 255 (mas sem limite inferior), você usaria clamp(myNumber, -Infinity, 255).
Kat
115

Se você criar uma função como aquela em que minimize(4, 10)retorna 10 , eu diria que isso é desaconselhável, porque seus colegas programadores podem estrangular você.

(Ok, talvez eles não o estrangulem literalmente até a morte, mas sério ... Não faça isso.)

Telastyn
fonte
2
Também é perfeitamente possível que, quando você voltar a trabalhar nesse código em alguns anos, você mesmo passará várias horas tentando descobrir por que seus números estão errados quando você chama minimizar ...
Paddy
Aww ... essa resposta costumava ter um comentário muito legal (e bastante votado). (Ele também foi o primeiro comentário sobre a resposta, o que ajudou o fluxo humorístico.)
TOOGAM
Continuo querendo receber cartões perfurados e riscar a DO NOT(fora de "NÃO fie, dobre ou mutile"). Alguém que implementasse algo assim receberia um cartão.
Clockwork-Muse
26

Aliasing 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 fazem isso o tempo todo .

No entanto, é uma má idéia usar termos de maneira contrária ao uso comum, como o seu exemplo, onde você imagina que max e min devem ser invertidos. É confuso para outros programadores, e você fará um desserviço treinando a si mesmo para continuar interpretando esses termos de maneira não-padrão.

Portanto, no seu caso, abandone o jargão "mínimo / máximo" que achar confuso 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 esse código, estará se lembrando de como min e max são usados ​​na sua linguagem de programação. Eventualmente, isso fará sentido em sua cabeça.

Tim Grant
fonte
4
Acho que você não entendeu bem a aplicação mine maxque confunde o OP. É quando miné usado para definir um limite superior fixo em algum valor. get_lower_valueseria tão contra-intuitivo nesta aplicação. Se eu escolhesse um nome alternativo para esta operação, eu o chamaria supremo , embora não tenha certeza de quantos programadores entenderiam isso imediatamente.
usar o seguinte código
3
@leftaroundabout: o supremo do conjunto {1, 2} é 2, portanto, não use o nome supremumda função get_lower_valuedefinida acima para chamar apenas min. Isso faz com que o próximo programador seja exatamente o mesmo problema que chamá-lo maximise. Eu sugiro chamá-lo apply_upper_bound, mas não tenho certeza de que seja perfeito. Ainda é estranho porque funciona da mesma maneira que você coloca os parâmetros, mas o nome implica que um dos parâmetros é "o valor" e o outro é "o limite", e que de alguma forma são diferentes.
9788 Steve Jobs
11
Eu acho que você está basicamente confiando no leitor para não estar muito familiarizado com a palavra supremo, para que eles entendam seu significado e não o significado em inglês. Não há problema em definir o jargão local para o seu código, mas o tipo de questão é o que fazer quando o jargão que você deseja contradiz diretamente um significado já familiar, e acho que você ainda está fazendo isso (por alguma versão de "Inglês" relevante apenas para aqueles que já estudou disciplinas STEM)
Steve Jessop
3
Essa política "Alias ​​uma função é boa, mas não tente alterar o significado dos termos existentes" é perfeita e lindamente expressa. Em segundo lugar, a idéia de que você não deve renomear de maneira confusa em relação às funções internas (e: mesmo que as funções internas sejam mal nomeadas / estúpidas / confusas) é uma idéia perfeita. No exemplo max / min que apareceu nesta página, é total confusão heh.
Fattie
11
OK, editei a resposta para usar "apply_upper_bound", que se encaixa no raciocínio do OP e evita a terminologia de sobrecarga. A verbosidade não é um problema. Não estou planejando escrever o nome do método perfeito aqui, apenas defina algumas regras básicas para nomear aliases. O OP pode editá-lo para o que ele achar mais claro e conciso.
Tim Grant
12

Eu amo essa pergunta. Vamos acabar com isso.

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

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

Além disso, você pode fazer muitas coisas em uma única linha atualmente.

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

Sim! Eles são totalmente! Um guru de codificação limpo os renomeia como "FunctionWhichReturnsTheLargestOfItsParameters" ou algo assim. Felizmente, temos documentação e (se você tiver sorte) IntelliSense e comentários para nos ajudar, para que qualquer pessoa que esteja confusa com os nomes possa ler o que deve fazer.

3: Você deve renomeá-los para outra coisa você mesmo.

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

Acrescenta significado e o chamador não precisa ou deseja saber como calcular o valor.

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

Não!! és maluco?! Mas sim, a pergunta sublinha o fato de que pessoas diferentes leem significados diferentes em nomes de funções e objetos. O que uma pessoa acha claro e convencional, a outra acha opaca e confusa. É por isso que temos comentários. Você deve 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.

Ewan
fonte
6
Engraçado! No seu exemplo você fez o erro exato da op está preocupado com: você quer o número de feriados a ser não maior que maxHolidaysAllowed, então você nee min (a, b)
Falco
14
Se o código limpo realmente sugere que nomes ridículos FunctionWhichReturnsTheLargestOfItsParameterssão uma coisa boa, não quero fazer parte dele.
David Hammen
11
@Falco lol oops !!!
Ewan
4
@ DavidHammen: na verdade não, pois nenhum estilo de programação real coloca a verruga FunctionWhichReturnsna frente de todas as funções (que não lançam uma exceção ou terminam). Você pode acabar com getMinimum, getLarger(ou getLargestcom mais de 2 entradas), porém, seguindo o conselho real, ao longo das linhas de que (a) funções puras e / ou "getters" deve usar a verruga get, palavras (b) inglês não devem ser abreviados em nomes. Claramente, isso é muito detalhado para aqueles que decidem chamar essas funções max.
9788 Steve Jobs
11
sim, veja o comentário de @ falco. Eu realmente não poderia corrigi-lo depois disso!
Ewan
4

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

O significado de maxé "o máximo de", mas seu entendimento "intuitivo" é algo como "o máximo de". Mas isso é simplesmente um entendimento errado da função, e alterar o nome de maxpara maximumnão comunica sua interpretação diferente. Mesmo que você acredite firmemente que os designers 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, pois ele comunica claramente a intenção, mesmo que apenas envolva min.

JacquesB
fonte
3

O que pode ser confuso é usar Capped no nome da sua função ou entender o que significa colocar uma tampa. É um limitador e não requer um máximo de nada.

Se lhe for pedido o menor, o menor ou o mais antigo, você acha que Max é a função apropriada?

Deixe min e max em paz. Faça testes para, pelo menos, corrigi-lo na segunda vez.

Se você precisar usar muito essas funções em seu projeto, terá uma dica para ajudá-lo a esclarecer qual delas usar. Mais ou menos como <ou>, a parte mais larga da boca está voltada para o valor maior.

JeffO
fonte
11
+1 para possível confusão na terminologia como um problema subjacente. Eu acho que a confusão começa no comentário "retorne a soma, com o máximo de 255", então ele acha que a maxfunção é mais apropriada, mas em termos de lógica miné o que ele está realmente procurando.
Revenant
2

Para responder à sua pergunta: Existe alguma outra razão pela qual isso seria desaconselhável? E 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 outras pessoas, a menos que sejam importados. (Joes.Utilities.)

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

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

Você está ficando confuso porque está tentando aplicar a lógica do seu cérebro a essas funções mín. / Máx. Em vez disso, basta falar em inglês. ifo contrário inputé o .greater than or equal to 255 then return 255returninput

Qual é:

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

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

Digno7
fonte
11
Um dos meus colegas de trabalho mais antigos costumava dizer "Deixe o computador pensar por você".
1

Enquanto eu entender o seu problema, ficaria relutante em fazer isso. Seria melhor simplesmente perfurar 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ê, às vezes eles lutem com sua intuição sobre a qual usar a qualquer momento. Se estou lendo um programa e vejo max (x, y), sei imediatamente o que ele faz. Se você criar sua própria função "alias", qualquer pessoa que esteja lendo seu código não saberá o que esse alias faz. Eles precisam encontrar sua função. Isso interrompe desnecessariamente o fluxo de leitura e força o leitor a pensar melhor para entender seu programa.

Se você tiver problemas para descobrir qual usar em algum momento, eu diria, adicione um comentário explicando isso. Então, se um futuro leitor estiver igualmente confuso, seu comentário deve esclarecer. Ou, se você fizer errado, mas o comentário explicar o que você estava tentando fazer, a pessoa que tentar depurá-lo terá uma pista.

Depois que você alias uma função, porque o nome entra em conflito com sua intuição ... esse é o único caso em que isso é um problema? Ou você vai alias outras funções? Talvez você esteja confuso com "ler" e ache mais fácil pensar nisso como "aceitar", altere "anexar" para "StringTogether", "arredondar" para "DropDecimals" etc. etc. Leve isso a um extremo ridículo e seus programas serão incompreensíveis.

De fato, anos atrás eu trabalhei com um programador que não gostou de toda a pontuação em C. Então ele escreveu várias macros para deixá-lo escrever "THEN" em vez de "{" e "END-IF" em vez de "}" e dezenas de outras substituições. Então, quando você tentou ler os programas dele, nem parecia mais C, era como ter que aprender um idioma totalmente novo. 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 o idioma e a biblioteca.

Dito isto, eu 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 objetivo da sua função não é criar um alias, mas encapsular um comportamento que por acaso é uma única função, isso pode ser bom e adequado. Quero dizer, se logicamente e inevitavelmente você precisar fazer um máximo nesse ponto do programa, basta ligar diretamente para max. Mas se você precisar executar algum cálculo que hoje exige um máximo, mas que possa ser modificado no futuro para fazer outra coisa, uma função intermediária será apropriada.

Jay
fonte
0

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

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

Se o seu idioma permitir, você pode tentar

return  calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)

return  CDsInStocked.ButNoMoreThen(CasesInStock)
Ian
fonte
0

Não.

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

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

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

Escondendo coisas com as quais você não se sente confortável, 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 mine maxtrabalhar.

Agent_L
fonte
-1

Tudo bem, e não é realmente contra-intuitivo usar Min, Max para controlar mais e menos. Isso também é feito usando:

  • piso () e teto ()
  • braçadeira, limite, limites
  • e, claro, mod com truncamento de alta ordem.

No firmware, ele remonta à MMX, que antecede os modernos gráficos 3D que se baseiam nesse extensivo recurso.

Substituir uma função padrão da indústria, mesmo localmente, me preocuparia, um nome derivado pode ser melhor. Os alunos de C ++ podem sobrecarregar sua turma obscura, talvez.

mckenzm
fonte
Sinto muito, o que há com a MMX?
Tobia Tesan
Eu estava pensando em "valores saturados", onde o resultado é limitado em um limite. A idéia era que o código poderia ser mais simples se não fosse necessário incluir testes de limite e transbordamento (em gráficos para evitar ciclismo ou tornar-se positivo ao ultrapassar os limites negativos). PSUBSB / PSUBSW. Eu admito que o mecanismo pode não ser o mesmo, mas o efeito e a intenção da função são.
Mckenzm
-1

É bom em alguns casos, mas não no seu exemplo, porque há maneiras muito melhores para a palavra-lo:
saturate, clamp, clip, etc.

Mehrdad
fonte
-1

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);
}
David Ferrer
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 valor mais alto de x ou pelo menos 10.

Junco
fonte
-1

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

Portanto, a resposta é SIM, é desaconselhável . Serve apenas como um apelido para a linguagem do seu cérebro.

Faísca
fonte