Является ли нежелательным создание функции, которая по существу переименовывает встроенную функцию?

40

Я запутался в функциях min и max в определенных контекстах.

В одном контексте, когда вы используете функции для получения большего или меньшего из двух значений, это не проблема. Например,

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

Легко. Но в другом контексте я запутался. Если я пытаюсь установить максимум или минимум, я получаю это задом наперед.

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

Нецелесообразно ли выполнять свои собственные функции следующим образом?

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

Очевидно, что использование встроенных функций будет быстрее, но для меня это кажется ненужной микрооптимизацией. Есть ли другая причина, по которой это было бы нецелесообразно? Как насчет группового проекта?

Devsman
источник
72
Если min и max ухудшают вашу читабельность, подумайте об обмене на обычные «если». Иногда стоит написать немного больше кода для лучшей читаемости.
Т. Сар - Восстановить Монику
23
одним коротким постом вы уничтожили всю концепцию «чистого кодирования». Я приветствую вас, сэр!
Эван
21
Возможно, вы можете рассмотреть функцию C ++ 11std::clamp или что-то подобное.
Руонг
15
Возможно, лучшие имена будут up_to(для min) и at_least(для max)? Я думаю, что они передают значение лучше, чем minimize, и т. Д., Хотя может потребоваться мгновение, чтобы понять, почему они коммутативны.
Варбо
59
minа maxтакже и также minimizeи maximizeсовершенно неправильные названия для функций, которые вы хотите написать. По умолчанию minи maxимеет гораздо больше смысла. Вы фактически ПОЛУЧИЛИ правильные имена функций. Эта операция называется фиксацией или укупоркой, и вы написали две функции укупорки. Я бы предложил capUpperBoundи capLowBound. Мне не нужно никому объяснять, что делает, это очевидно.
Slebetman

Ответы:

120

Как уже упоминали другие: не создавайте функцию с именем, похожим на имя встроенной, стандартной библиотеки или широко используемой функции, но изменяйте ее поведение. Можно привыкнуть к соглашению об именах, даже если оно на первый взгляд не имеет особого смысла, но невозможно будет рассуждать о функционировании вашего кода, как только вы представите те другие функции, которые делают то же самое, но имеют их имена поменялись местами.

Вместо «перегрузки» имен, используемых стандартной библиотекой, используйте новые имена, которые точно передают то, что вы имеете в виду. В вашем случае вы не заинтересованы в «минимуме». Скорее, вы хотите ограничить стоимость. Математически это та же самая операция, но семантически это не совсем так. Так почему бы не просто функция

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

он делает то, что нужно, и говорит об этом по названию. (Кроме того, можно реализовать capс точки зрения , minкак показано в timster «s ответ ).

Другое часто используемое имя функции clamp. Он принимает три аргумента и «зажимает» предоставленное значение в интервале, заданном двумя другими значениями.

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

Если вы используете такое общеизвестное имя функции, любой новый человек, присоединившийся к вашей команде (включая будущее, к которому вы вернетесь через код через некоторое время), быстро поймет, что происходит, вместо того, чтобы проклинать вас за то, что вы запутали их, сломав их ожидания относительно имен функций, которые, как они думали, они знали.

5gon12eder
источник
2
Вы также можете назвать его getValueNotBiggerThan (x, limit), это правильный подход, и при достойном компиляторе функция будет встроена, и полученный машинный код будет точно таким же, как при использовании встроенных модулей
Falco
20
@Falco Я бы сказал, что «кепка» является почти синонимом «получить ценность не больше, чем», но я не собираюсь рисовать этот велосипедный сарай сегодня. ;-)
5gon12eder
1
clampшироко используется во всех видах обработки сигналов и аналогичных операциях (обработка изображений и т. д.), так что это определенно то, с чем я бы тоже столкнулся. Хотя я бы не сказал, что это требует верхних и нижних границ: я видел это довольно часто только для одного направления.
Во
1
Собирался упомянуть clampтоже. И если оно написано правильно, вы можете просто использовать границу бесконечности / отрицательной бесконечности, когда хотите, чтобы она была ограничена только одним способом. Например, чтобы убедиться, что число не больше 255 (но не нижней границы), вы должны использовать clamp(myNumber, -Infinity, 255).
Кат
115

Если вы сделаете такую ​​функцию, которая minimize(4, 10)возвращает 10 , то я бы сказал, что это нежелательно, потому что ваши коллеги-программисты могут вас задушить.

(Хорошо, может быть, они буквально не задушат тебя до смерти, но серьезно ... Не делай этого.)

Telastyn
источник
2
Также вполне возможно, что когда вы вернетесь к работе над этим кодом через несколько лет, вы сами потратите несколько часов, пытаясь выяснить, почему ваши цифры неверны, когда вы вызываете минимизацию ...
Пэдди
Оу ... у этого ответа был действительно классный (и довольно высоко оцененный) комментарий. (Это был также первый комментарий к ответу, который помог юмористическому потоку.)
TOOGAM
Я продолжаю хотеть получать перфокарты и вычеркивать DO NOT(из "НЕ Шпинделя, Сгиба, или Сломать"). Кто-то, кто реализовал что-то подобное, получил бы карточку.
Заводная муза
26

Псевдоним функции - это хорошо, но не пытайтесь изменить значение существующих терминов.

Можно создать псевдоним функции - обычные библиотеки делают это постоянно .

Тем не менее, это плохая идея использовать термины, противоречащие обычному употреблению, как, например, ваш пример, где, по вашему мнению, нужно переключать max и min. Это сбивает с толку других программистов, и вы будете оказывать себе медвежью услугу, обучая себя нестандартному толкованию этих терминов.

Так что в вашем случае откажитесь от выражения «минимальный / максимальный», которое вас смущает, и создайте свой собственный, легкий для понимания код.

Рефакторинг вашего примера:

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


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

В качестве дополнительного бонуса, каждый раз, когда вы смотрите на этот код, вы будете напоминать себе, как min и max используются в вашем языке программирования. В конце концов, это обретет смысл в вашей голове.

Тим Грант
источник
4
Я думаю, что вы неправильно поняли применение minи maxчто сбивает с толку ОП. Это когда minиспользуется, чтобы установить фиксированную верхнюю границу для некоторого значения. get_lower_valueбыло бы так же нелогично в этом приложении. Если бы я выбрал альтернативное имя для этой операции, я бы назвал его супремумом , хотя я не уверен, сколько программистов сразу это поймут.
оставлено около
3
@leftaroundabout: верхний предел множества {1, 2} равен 2, поэтому не используйте имя supremumдля функции get_lower_value, определенной выше, для простого вызова min. Это вызывает у следующего программиста ту же проблему, что и при его вызове maximise. Я бы предложил позвонить apply_upper_bound, но я не уверен, что это идеально. Это все еще странно, потому что работает одинаково, независимо от того, как вы помещаете параметры, но название подразумевает, что один из параметров является «значением», а другой - «связанным», и что они как-то отличаются.
Стив Джессоп
1
Я думаю, что вы, в сущности, полагаетесь на то, что читатель не слишком хорошо знаком со словом supremum, так что они воспринимают ваше значение, а не английское значение. Хорошо определить локальный жаргон для вашего кода, но вопрос в том, что делать, если нужный вам жаргон прямо противоречит уже знакомому значению, и я боюсь, что вы все еще делаете это (для какая-то версия «английского» актуальна только для тех, кто изучал предметы STEM)
Стив Джессоп
3
Эта политика «алиасинг функции - это хорошо, но не пытайтесь изменить значение существующих терминов» - прекрасно и красиво выражена. Во-вторых, идея о том, что вы не должны переименовывать таким образом, который вводит в заблуждение относительно встроенных функций (и: даже если встроенные функции имеют плохое имя / глупо / сбивает с толку), является идеальной идеей. В качестве примера макс / мин, который появился на этой странице, это полная путаница, хе.
Толстяк
1
Хорошо, я отредактировал ответ, чтобы использовать «apply_upper_bound», который соответствует аргументам OP и избегает терминологии перегрузки. Многословие не проблема. Я не собираюсь писать здесь идеальное имя метода, просто установите некоторые основные правила для именования псевдонимов. ОП может отредактировать его так, как он считает наиболее понятным и лаконичным.
Тим Грант
12

Мне нравится этот вопрос. Давайте разберемся с этим хотя.

1: следует ли обернуть одну строку кода?

Да, я могу привести множество примеров, где вы можете сделать это. Возможно, вы вводите типизированные параметры или скрываете конкретную реализацию за интерфейсом. В вашем примере вы по сути скрываете статический вызов метода.

Кроме того, вы можете сделать много вещей в одну строку в эти дни.

2: Запутывают ли имена «Мин» и «Макс»

Да! Они полностью такие! Чистый гуру кодирования переименовал бы их в «FunctionWhichReturnsTheLargestOfItsParameters» или что-то в этом роде. К счастью, у нас есть документация и (если вам повезет) IntelliSense и комментарии, чтобы помочь нам, чтобы любой, кто смущен именами, мог прочитать о том, что они должны делать.

3: Должны ли вы переименовать их во что-то другое.

Да, пойти на это. Например, вы могли бы иметь:

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

Это добавляет смысл, и вызывающая сторона не должна или не хочет знать, как рассчитать значение.

4: Если вы переименуете «мин», чтобы «максимизировать»

Нет !! вы с ума сошли?! Но да, этот вопрос подчеркивает тот факт, что разные люди читают разные значения в именах функций и объектов. То, что один человек находит ясным и условным, другой находит непрозрачным и запутанным. Вот почему у нас есть комментарии. Вместо этого вы должны написать:

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

Тогда, когда кто-то читает

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

они знают, что вы сделали ошибку.

Ewan
источник
6
Веселая! В вашем примере вы допустили точную ошибку, с которой связана операция: вы хотите, чтобы число выходных не превышало maxHolidaysAllowed, так что вам нужно минимум min (a, b)
Falco
14
Если чистый код действительно предполагает, что нелепые имена, такие как FunctionWhichReturnsTheLargestOfItsParametersхорошая вещь, я не хочу участвовать в этом.
Дэвид Хаммен
1
@ Фалько лол упс !!!
Эван
4
@DavidHammen: на самом деле это не так, поскольку ни один из настоящих стилей программирования не ставит бородавку FunctionWhichReturnsвпереди каждой функции (которая не выдает исключение и не завершает работу). Вы могли бы в конечном итоге с getMinimum, getLarger(или getLargestс более чем 2 входами) , хотя, следуя реальные советы вдоль линий , что (а) чистые функции и / или «добытчики» следует использовать бородавку get, (б) английские слова не должны быть сокращенными в имена. Понятно, что это слишком многословно для тех, кто решает вызывать такие функции max.
Стив Джессоп
1
да, смотрите комментарий @ falco. Я не мог исправить это после этого!
Эван
4

Нет . Не создавайте функции с именами, очень похожими на встроенные функции, но на самом деле это не так . Это может показаться вам интуитивно понятным, но будет очень запутанным для других разработчиков, и даже для вас самих в будущем, когда у вас будет больше опыта.

Значение max«максимум», но ваше «интуитивное» понимание - это что-то вроде «максимум». Но это просто неправильное понимание функции, и изменение названия с maxна maximumне отражает вашу другую интерпретацию. Даже если вы твердо уверены, что разработчики языка допустили ошибку, не делайте что-то подобное.

Но было бы неплохо изменить название, чтобы сказать, cap(x, limit)как было предложено, поскольку оно явно передает намерение, даже если оно просто завершает min.

JacquesB
источник
3

Что может сбить вас с толку, так это использовать Capped в названии вашей функции или ваше понимание того, что означает использование cap. Это ограничитель и не требует максимум ничего.

Если вас спрашивают о самом низком, самом маленьком или самом раннем, чувствуете ли вы, что Макс - подходящая функция?

Оставьте мин и макс в покое. Напишите тесты, чтобы, по крайней мере, вы исправили это во второй раз.

Если вам нужно так много использовать эти функции в вашем проекте, вы получите какую-то подсказку, чтобы помочь вам уточнить, какую из них использовать. Вроде как <или>, широкая часть рта обращена к большему значению.

JeffO
источник
1
+1 за возможную путаницу в терминологии в качестве основной проблемы. Я думаю, что путаница начинается с комментария «верните сумму с максимумом 255», поэтому он считает, что maxфункция более подходящая, но логическая minвещь - это то, что он на самом деле ищет.
Revenant
2

Чтобы ответить на ваш вопрос: есть ли какая-либо другая причина, по которой это было бы нежелательно? Как насчет группового проекта? Это имеет смысл, что вы хотите свои собственные функции, что не проблема. Просто убедитесь, что они находятся в вашем собственном классе помощников и не могут легко вызываться другими, если они не импортируют его. (Joes.Utilities.)

Но, чтобы снова взглянуть на вашу проблему, я бы подумал:

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

Вы запутались, потому что пытаетесь применить свою мозговую логику к этим минимальным / максимальным функциям. Вместо этого просто говорите по-английски. в противном случае .ifinputgreater than or equal to 255 then return 255returninput

Который:

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

Мое мнение. Вы используете функции max \ min по неправильным причинам, скорость этих вещей незначительна. Делай то, что имеет смысл.

Worthy7
источник
1
Один из моих старших коллег всегда говорил: «Пусть компьютер думает за вас».
1

Хотя я понимаю вашу проблему, я бы не хотел этого делать. Было бы лучше просто просверлить в вашем черепе то, что делают min () и max ().

Большинство программистов знают, что делают функции min () и max () - даже если, как и вы, они иногда борются со своей интуицией, которую можно использовать в любой момент времени. Если я читаю программу и вижу max (x, y), я сразу знаю, что она делает. Если вы создадите свою собственную функцию «псевдонима», то любой, кто читает ваш код, не будет знать, что делает этот псевдоним. Они должны найти вашу функцию. Это излишне нарушает поток чтения и заставляет читателя задуматься, чтобы понять вашу программу.

Если у вас возникли проблемы с определением, какой из них использовать в какой-то момент, я бы сказал, добавьте комментарий, объясняющий это. Тогда, если будущий читатель так же запутался, ваш комментарий должен прояснить ситуацию. Или, если вы делаете это неправильно, но комментарий объясняет, что вы пытались сделать, у человека, пытающегося его отладить, будет подсказка.

Как только вы создадите псевдоним функции, потому что имя конфликтует с вашей интуицией ... это единственный случай, когда это проблема? Или вы собираетесь использовать псевдонимы для других функций? Может быть, вас смущает слово «чтение», и вам легче думать о нем как о «принять», вы меняете «добавить» на «StringTogether», «округляет» на «DropDecimals» и т. Д. И т. Д. Примите это до смешного крайности и ваши программы будут непонятны.

Действительно, много лет назад я работал с программистом, которому не нравились все знаки препинания в C. Поэтому он написал несколько макросов, чтобы позволить ему писать «THEN» вместо «{» и «END-IF» вместо «}» и десятки других таких замен. Итак, когда вы пытались читать его программы, это уже не выглядело как C, это было похоже на необходимость выучить совершенно новый язык. Я не помню, переводил ли «И» на «&» или «&&» - и в этом суть. Вы подрываете инвестиции людей в изучение языка и библиотеки.

Тем не менее, я бы не сказал, что функция, которая ничего не делает, но вызывает стандартную библиотечную функцию, обязательно плохая. Если целью вашей функции является не создание псевдонима, а инкапсуляция поведения, которое оказывается единственной функцией, это может быть хорошо и правильно. Я имею в виду, что если логически и неизбежно вам нужно выполнить max в этой точке программы, просто вызовите max напрямую. Но если вам нужно выполнить какое-то вычисление, которое сегодня требует максимума, но которое может быть изменено в будущем, чтобы сделать что-то еще, тогда уместна промежуточная функция.

сойка
источник
0

Можно переименовывать встроенные функции при условии, что новые имена сделают ваш код понятным и понятным для всех. (Если вы используете C / C ++, не используйте #define, так как это затрудняет понимание того, что происходит.) Имя функции должно действовать как комментарий, объясняющий, что делает вызывающий код и почему он делает с ,

Вы не единственный, у кого была эта проблема с min и max, однако я пока не вижу хорошего общего решения, которое работает во всех доменах. Я думаю, что одна проблема с именованием этих функций заключается в том, что два аргумента имеют разные логические значения, но представлены как имеющие одно и то же значение.

Если ваш язык позволяет это, вы можете попробовать

return  calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)

return  CDsInStocked.ButNoMoreThen(CasesInStock)
Ян
источник
0

Нет.

Вы не пишите свои обертки. Имена этих фантиков не очень значимы.

То, что вы пытаетесь сделать, - это некая кодировка. Вы изобретаете дополнительный слой, который служит 2 целям:

  1. Другие люди не могут понять ваш код.
  2. Вы никогда не научитесь понимать код других людей.

Скрывая вещи, которые вас не устраивают, вы наносите вред своему коду сейчас и себе в будущем. Вы не можете расти, оставаясь в своей зоне комфорта. Что вам нужно, это научиться minи maxработать.

Agent_L
источник
-1

Это нормально, и на самом деле не противоинтуитивно использовать Мин, Макс, чтобы обуздать и недолить. Это также делается с помощью:

  • пол () и потолок ()
  • зажим, предел, границы
  • и конечно мод с усечением высокого порядка.

В прошивке он восходит к MMX, который сам по себе предшествует современной 3D-графике, которая опирается на этот экстенсивлей.

Меня беспокоит замена стандартной отраслевой функции даже локально, может быть лучше использовать производное имя. Возможно, студенты C ++ могут перегружать свой непонятный класс.

mckenzm
источник
Простите, а что с MMX?
Тобиа Тесан
Я думал о «насыщенных ценностях», где результат ограничен до предела. Идея заключалась в том, что код мог бы быть проще, если бы в него не входили тесты на границу и переполнение (в графике, чтобы избежать зацикливания или перехода в положительное положение, преодолевая пределы отрицательного значения). PSUBSB / PSUBSW. Я допускаю, что механизм может не быть тем же самым, но эффект и назначение функции есть.
mckenzm
-1

Это хорошо в некоторых случаях, но не в вашем примере, потому что есть гораздо лучшие способы слова это:
saturate, clamp, clipи т.д.

Mehrdad
источник
-1

Я бы лучше создал универсальную функцию с именем «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;
}

или с использованием 'min' и '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

Как насчет вызова ваших функций:

atmost(x,255): верните самое низкое из x или 255 самое большее.

atleast(10,x): вернуть большее из x или хотя бы 10.

камыш озерный
источник
-1

min(x+y, MAX_VALUE); будет иметь гораздо больше смысла, чем myCustomFunction(x, y);

Так что ответ ДА, это не желательно . Он служит только псевдонимом для вашего мозга.

искра
источник