Czy nie jest wskazane tworzenie funkcji, która zasadniczo zmienia nazwę wbudowanej funkcji?

40

W niektórych kontekstach jestem zdezorientowany funkcjami min i max.

W jednym kontekście, gdy używasz funkcji do przyjęcia większej lub mniejszej z dwóch wartości, nie ma problemu. Na przykład,

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

Łatwo. Ale w innym kontekście jestem zdezorientowany. Jeśli próbuję ustawić maksimum lub minimum, otrzymuję je wstecz.

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

Czy nie jest wskazane, aby tworzyć własne funkcje w następujący sposób?

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

Oczywiście korzystanie z wbudowanych będzie szybsze, ale wydaje mi się to niepotrzebną mikrooptymalizacją. Czy jest jakiś inny powód, dla którego byłoby to niewskazane? A co z projektem grupowym?

Devsman
źródło
72
Jeśli min i max mają negatywny wpływ na czytelność, rozważ zamianę, a potem wybierz regularne „jeśli”. Czasami warto napisać nieco więcej kodu, aby uzyskać lepszą czytelność.
T. Sar - Przywróć Monikę
23
jednym krótkim postem zniszczyłeś całą koncepcję „czystego kodowania”. Pozdrawiam pana!
Ewan
21
Być może można rozważyć w c ++ 11 std::clampfunkcji lub coś podobnego.
rwong
15
Być może lepsze nazwy to up_to(dla min) i at_least(dla max)? Myślę, że przekazują znaczenie lepiej niż minimizeitp., Chociaż może chwilę zastanowić się, dlaczego są przemienni.
Warbo,
59
mina maxtakże minimizei maximizesą całkowicie błędnymi nazwami funkcji, które chcesz napisać. Domyślne mini maxmają znacznie większy sens. W rzeczywistości PRAWIE masz prawidłowe nazwy funkcji. Ta operacja nazywa się zaciskaniem lub zamykaniem i napisałeś dwie funkcje zamykania. Sugerowałbym capUpperBoundi capLowBound. Nie muszę nikomu wyjaśniać, co robi, co jest oczywiste.
slebetman

Odpowiedzi:

120

Jak już wspomnieli inni: nie twórz funkcji o nazwie podobnej do wbudowanej biblioteki standardowej lub ogólnie używanej funkcji, ale zmień jej zachowanie. Można przyzwyczaić się do konwencji nazewnictwa, nawet jeśli na pierwszy rzut oka nie ma to większego sensu, ale nie będzie możliwe uzasadnienie działania kodu po wprowadzeniu innych funkcji, które robią to samo, ale mają ich nazwiska zamieniły się.

Zamiast „przeciążać” nazwy używane przez standardową bibliotekę, używaj nowych nazw, które dokładnie odzwierciedlają to, co masz na myśli. W twoim przypadku tak naprawdę nie interesuje Cię „minimum”. Zamiast tego chcesz ograniczyć wartość. Matematycznie jest to ta sama operacja, ale semantycznie nie do końca. Dlaczego więc nie tylko funkcja

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

robi to, co jest potrzebne i mówi to po nazwie. (Można również realizować capw warunkach minjak pokazano w timster „s odpowiedzi ).

Inną często używaną nazwą funkcji jest clamp. Pobiera trzy argumenty i „zaciska” podaną wartość w interwale określonym przez pozostałe dwie wartości.

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

Jeśli używasz tak powszechnie znanej nazwy funkcji, każda nowa osoba dołączająca do Twojego zespołu (w tym przyszłość, którą wrócisz do kodu po chwili) szybko zrozumie, co się dzieje, zamiast przeklinać cię za pomylenie ich przez złamanie ich oczekiwania dotyczące nazw funkcji, które według nich znają.

5gon12eder
źródło
2
Możesz również nazwać go getValueNotBiggerThan (x, limit), jest to właściwe podejście, a przy przyzwoitym kompilatorze funkcja zostanie wstawiona, a wynikowy kod maszynowy będzie dokładnie taki sam, jak przy użyciu wbudowanych
Falco
20
@Falco Powiedziałbym, że „czapka” jest prawie synonimem „uzyskać wartość nie większą niż”, ale nie zamierzam dzisiaj malować tej szopy na rowery. ;-)
5gon12eder
1
clampjest szeroko stosowany we wszelkiego rodzaju przetwarzaniu sygnałów i podobnych operacjach (przetwarzanie obrazu itp.), więc zdecydowanie to też bym wybrał. Chociaż nie powiedziałbym, że wymaga górnej i dolnej granicy: widziałem to dość często tylko w jednym kierunku.
Voo
1
Miałem też wspomnieć clamp. A jeśli jest napisany poprawnie, możesz po prostu użyć granicy nieskończoności / ujemnej nieskończoności, gdy chcesz, aby była związana tylko w jedną stronę. Na przykład, aby upewnić się, że liczba nie jest większa niż 255 (ale nie dolna granica), należy użyć clamp(myNumber, -Infinity, 255).
Kat
115

Jeśli wykonasz taką funkcję, gdzie minimize(4, 10)zwraca 10 , to powiedziałbym, że jest to niewskazane, ponieważ inni programiści mogą cię udusić.

(Okej, może nie dosłownie cię uduszą na śmierć, ale poważnie ... Nie rób tego.)

Telastyn
źródło
2
Całkowicie możliwe jest również to, że kiedy wrócisz do pracy nad tym kodem za kilka lat, sam spędzisz kilka godzin, próbując dowiedzieć się, dlaczego twoje liczby są błędne, gdy dzwonisz, aby zminimalizować ...
Paddy
Aww ... ta odpowiedź miała naprawdę fajny (i dość wysoko głosowany) komentarz. (Był to również pierwszy komentarz do odpowiedzi, który pomógł w humorystycznym przepływie.)
TOOGAM
Wciąż chcę dostać karty ciosów i wykreślić DO NOT(z „NIE Wrzeciona, Nie pasuj ani nie okaleczaj”). Ktoś, kto zaimplementował coś takiego, otrzymałby kartę.
Clockwork-Muse
26

Aliasowanie funkcji jest w porządku, ale nie próbuj zmieniać znaczenia istniejących terminów

Tworzenie aliasu funkcji jest w porządku - wspólne biblioteki robią to cały czas .

Jednak złym pomysłem jest używanie terminów w sposób sprzeczny z powszechnym użyciem, np. W twoim przykładzie, w którym według ciebie maksymalne i minimalne wartości powinny być odwrócone. Jest to mylące dla innych programistów, a wyrządzicie sobie krzywdę, ucząc się interpretowania tych terminów w niestandardowy sposób.

Więc w twoim przypadku porzuć żargon „minimalny / maksymalny”, który uważasz za mylący, i stwórz własny, łatwy do zrozumienia kod.

Refaktoryzacja Twojego przykładu:

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


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

Jako dodatkowy bonus za każdym razem, gdy spojrzysz na ten kod, będziesz sobie przypominał, w jaki sposób wartości minimalne i maksymalne są używane w języku programowania. W końcu będzie to miało sens w twojej głowie.

Tim Grant
źródło
4
Myślę, że źle zrozumiałeś zastosowanie mini maxktóre myli OP. To kiedy minjest używany do ustawienia stałej górnej granicy na jakąś wartość. get_lower_valuebyłoby w tej aplikacji równie sprzeczne z intuicją. Gdybym miał wybrać alternatywną nazwę dla tej operacji, nazwałbym to supremum , chociaż nie jestem pewien, ilu programistów natychmiast to zrozumie.
leftaroundabout
3
@leftaroundabout: supremum zestawu {1, 2} wynosi 2, więc nie używaj nazwy supremumfunkcji get_lower_valuezdefiniowanej powyżej tylko do wywołania min. Powoduje, że następny programista ma dokładnie ten sam problem co wywołanie go maximise. Sugerowałbym to nazwać apply_upper_bound, ale nie jestem pewien, czy to idealne. To wciąż dziwne, ponieważ działa tak samo, niezależnie od tego, w jaki sposób umieścisz parametry, ale nazwa sugeruje, że jeden z parametrów jest „wartością”, a drugi jest „związany” i że są one w jakiś sposób różne.
Steve Jessop,
1
Myślę, że w zasadzie polegasz na tym, że czytelnik nie zna zbytnio słowa supremum, aby przyjęli twoje znaczenie, a nie angielskie. Dobrze jest zdefiniować lokalny żargon w kodzie, ale w pewnym sensie chodzi o to, co zrobić, gdy żargon, którego chcesz, jest sprzeczny z już znanym znaczeniem i obawiam się, że myślę, że nadal to robisz (dla niektóre wersje „angielskiego” istotne tylko dla tych, którzy studiowali przedmioty STEM)
Steve Jessop
3
Ta zasada „Aliasing funkcji jest w porządku, ale nie próbuj zmieniać znaczenia istniejących terminów” jest idealna i pięknie wyrażona. Po drugie, pomysł, aby nie zmieniać nazwy w sposób mylący w stosunku do wbudowanych funkcji (i: nawet jeśli wbudowane funkcje są źle nazwane / głupie / mylące) jest idealnym pomysłem. Jeśli chodzi o maksymalny / minimalny przykład, który pojawił się na tej stronie, to totalne zamieszanie heh.
Fattie,
1
OK, zredagowałem odpowiedź, aby użyć „Apply_upper_bound”, która zarówno pasuje do rozumowania OP, jak i pozwala uniknąć przeładowania terminologii. Gadatliwość nie stanowi problemu. Nie zamierzam tutaj pisać idealnej nazwy metody, po prostu ustal podstawowe zasady nazewnictwa aliasów. OP może edytować to, co uzna za najbardziej jasne i zwięzłe.
Tim Grant,
12

Uwielbiam to pytanie. Rozbijmy to jednak.

1: Czy powinieneś zawinąć pojedynczy wiersz kodu?

Tak, mogę wymyślić wiele przykładów, w których możesz to zrobić. Może wymuszasz wpisywanie parametrów lub ukrywasz konkretną implementację za interfejsem. W twoim przykładzie zasadniczo ukrywasz statyczne wywołanie metody.

Dodatkowo w dzisiejszych czasach możesz robić wiele rzeczy w jednym wierszu.

2: Czy nazwy „Min” i „Max” są mylące?

Tak! Oni są całkowicie! Czysty guru kodujący zmieniłby ich nazwy na „FunctionWhichReturnsTheLargestOfItsParameters” lub coś w tym rodzaju. Na szczęście mamy dokumentację i (jeśli masz szczęście) IntelliSense i komentarze, które nam pomogą, aby każdy, kto jest zdezorientowany nazwami, mógł przeczytać o tym, co powinien zrobić.

3: Jeśli sam zmienisz nazwę na coś innego.

Tak, idź po to. Na przykład możesz mieć:

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

Dodaje sens, a osoba dzwoniąca nie musi lub nie chce wiedzieć, jak obliczyć wartość.

4: Jeśli zmienisz nazwę „min” na „maksymalizuj”

Nie!! oszalałeś?! Ale tak, pytanie podkreśla, że ​​różni ludzie odczytują różne znaczenia nazw funkcji i obiektów. To, co jedna osoba uważa za jasne i konwencjonalne, inne jest niejasne i zagmatwane. Dlatego mamy komentarze. Zamiast tego powinieneś napisać:

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

Potem, gdy ktoś czyta

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

wiedzą, że popełniłeś błąd.

Ewan
źródło
6
Zabawny! W swoim przykładzie popełniłeś dokładny błąd, którego dotyczy operacja: chcesz, aby liczba świąt nie była większa niż maxHolidaysAllowed, więc musisz min (a, b)
Falco
14
Jeśli czysty kod naprawdę sugeruje, że absurdalne nazwy, takie jak, FunctionWhichReturnsTheLargestOfItsParameterssą dobre, nie chcę tego brać w tym udziału.
David Hammen
1
@Falco lol oops !!!
Ewan
4
@DavidHammen: tak nie jest, ponieważ żaden prawdziwy styl programowania nie umieszcza brodawki FunctionWhichReturnsna przodzie każdej funkcji (która nie rzuca wyjątku ani nie kończy). Można skończyć z getMinimum, getLarger(lub getLargestz więcej niż 2 wejścia), choć, wykonując prawdziwe porady wzdłuż linii, które (a) czyste funkcje i / lub „pozyskiwaniu” należy użyć brodawkę get, (b) angielskie słowa nie powinna być podana w skrócie nazwy. Oczywiście jest to zbyt szczegółowe dla tych, którzy decydują się na wywołanie takich funkcji max.
Steve Jessop,
1
tak, patrz komentarz @ falco. po tym nie mogłem tego naprawdę naprawić!
Ewan
4

Nie . Nie twórz funkcji o nazwach bardzo podobnych do funkcji wbudowanych, ale faktycznie robi to odwrotnie . Może się to wydawać intuicyjne, ale będzie bardzo mylące dla innych programistów, a nawet dla ciebie w przyszłości, gdy będziesz miał więcej doświadczenia.

Znaczenie maxto „maksimum”, ale twoje „intuicyjne” rozumienie przypomina „maksimum”. Ale to po prostu złe zrozumienie funkcji, a zmiana nazwy z maxna maximumnie przekazuje twojej odmiennej interpretacji. Nawet jeśli mocno wierzysz, że projektanci języków popełnili błąd, nie rób czegoś takiego.

Ale zmiana nazwy na tak, cap(x, limit)jak sugerowano, byłaby w porządku, ponieważ wyraźnie przekazuje zamiar, nawet jeśli tylko się zawija min.

JacquesB
źródło
3

To, co może być mylące, to użycie Capped w nazwie funkcji lub zrozumienie, co oznacza wprowadzenie ograniczenia. Jest to ogranicznik i nie wymaga maksimum czegokolwiek.

Jeśli pytasz o najniższą, najmniejszą lub najwcześniejszą, czy uważasz, że Max jest odpowiednią funkcją?

Pozostaw min i maks w spokoju. Napisz testy, aby przynajmniej raz poprawić.

Jeśli musisz tak często korzystać z tych funkcji w swoim projekcie, znajdziesz jakąś wskazówkę, która pomoże ci wyjaśnić, której z nich użyć. Coś w rodzaju <lub>, szeroka część ust jest skierowana w stronę większej wartości.

JeffO
źródło
1
+1 za możliwe zamieszanie terminologiczne jako podstawowy problem. Myślę, że zamieszanie zaczyna się od komentarza „zwróć sumę, z maksimum 255”, więc uważa, że maxfunkcja jest bardziej odpowiednia, ale logicznie minrzeczą, której tak naprawdę szuka.
Revenant
2

Aby odpowiedzieć na twoje pytanie: Czy jest jakiś inny powód, dla którego byłoby to niewskazane? A co z projektem grupowym? Ma to sens, że chcesz mieć własne funkcje, co nie stanowi problemu. Upewnij się tylko, że należą do twojej klasy pomocniczej i nie są łatwe do wywołania dla innych, chyba że ją zaimportują. (Joes.Utilities.)

Ale żeby spojrzeć ponownie na twój problem, w zasadzie zamiast tego pomyślałbym:

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

Czujesz się zagubiony, ponieważ próbujesz zastosować logikę mózgu do tych funkcji min / maks. Zamiast tego po prostu mów po angielsku. jest inaczej .ifinputgreater than or equal to 255 then return 255returninput

Który jest:

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

Moja opinia. Idziesz do funkcji max \ min z niewłaściwych powodów, prędkość tych rzeczy jest znikoma. Rób to, co ma sens.

Worthy7
źródło
1
Jeden z moich starszych współpracowników zawsze mawiał: „Pozwól komputerowi myśleć za Ciebie”.
1

Rozumiem twój problem, ale niechętnie to zrobię. Lepiej po prostu wiercić w czaszce, co robią min () i max ().

Większość programistów wie, co robią funkcje min () i max () - nawet jeśli, podobnie jak ty, czasami zmagają się z intuicją, z której korzystać w danym momencie. Jeśli czytam program i widzę max (x, y), od razu wiem, co on robi. Jeśli utworzysz własną funkcję „alias”, to nikt inny czytający Twój kod nie będzie wiedział, co robi ten alias. Muszą znaleźć twoją funkcję. Niepotrzebnie przerywa przepływ czytania i zmusza czytelnika do dodatkowego myślenia, aby zrozumieć Twój program.

Jeśli masz problem z ustaleniem, którego użyć w pewnym momencie, dodam komentarz wyjaśniający to. Następnie, jeśli przyszły czytelnik jest podobnie zdezorientowany, twój komentarz powinien to wyjaśnić. Lub jeśli zrobisz to źle, ale komentarz wyjaśnia, co próbujesz zrobić, osoba próbująca go debugować będzie miała wskazówkę.

Po aliasowaniu funkcji, ponieważ nazwa koliduje z intuicją ... czy to jedyny przypadek, w którym jest to problem? A może wybierasz alias innych funkcji? Być może jesteś zdezorientowany przez „czytaj” i łatwiej ci myśleć o tym jako o „akceptuj”, zmieniasz „dopisz” na „StringTogether”, „zaokrąglasz” do „DropDecimals” itp. Przenieś to do absurdalnej skrajności a wasze programy będą niezrozumiałe.

Rzeczywiście, lata temu pracowałem z programistą, który nie lubił wszystkich znaków interpunkcyjnych w C. Więc napisał kilka makr, aby pozwolić mu napisać „TO” zamiast „{” i „END-IF” zamiast „}” i dziesiątki innych takich zamian. Więc kiedy próbowałeś czytać jego programy, nie wyglądało to już nawet na C, to było jak nauka nowego języka. Nie pamiętam teraz, czy „AND” przetłumaczone na „&” czy „&&” - i o to chodzi. Podważasz inwestycje, które ludzie włożyli w naukę języka i biblioteki.

To powiedziawszy, nie powiedziałbym, że funkcja, która nie robi nic poza wywołaniem standardowej funkcji bibliotecznej, jest koniecznie zła. Jeśli celem twojej funkcji nie jest tworzenie aliasu, ale zamknięcie zachowania, które akurat jest pojedynczą funkcją, może to być dobre i właściwe. Mam na myśli, że jeśli logicznie i nieuchronnie musisz zrobić maksimum w tym punkcie programu, to po prostu wywołaj maksimum bezpośrednio. Ale jeśli musisz wykonać jakieś obliczenia, które dzisiaj wymagają maksimum, ale które mogą zostać zmodyfikowane w przyszłości, aby zrobić coś innego, odpowiednia jest funkcja pośrednia.

Sójka
źródło
0

Zmiana nazw wbudowanych funkcji jest w porządku, pod warunkiem, że nowe nazwy znacznie upraszczają kod i nikt go nie zrozumie. (Jeśli używasz C / C ++, nie używaj #define, ponieważ utrudnia to zobaczenie, co się dzieje.) Nazwa funkcji powinna działać jak komentarz wyjaśniający, co robi kod wywołujący i dlaczego .

Nie jesteś jedyną osobą, która miała ten problem z parametrami min i max, jednak wciąż nie widzę dobrego ogólnego rozwiązania, które działa we wszystkich domenach. Myślę, że jednym z problemów związanych z nazywaniem tych funkcji jest to, że dwa argumenty mają różne logiczne znaczenia, ale są przedstawiane w ten sam sposób.

Jeśli Twój język na to pozwala, możesz spróbować

return  calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)

return  CDsInStocked.ButNoMoreThen(CasesInStock)
Ian
źródło
0

Nie.

Nie piszesz swoich opakowań. Nazwy tych opakowań nie są bardzo znaczące.

To, co próbujesz zrobić, to miłe zaciemnienie kodu. Wymyślasz dodatkową warstwę, która służy 2 celom:

  1. Inne osoby nie mogą zrozumieć Twojego kodu.
  2. Nigdy nie uczysz się rozumieć kod innych ludzi.

Ukrywając rzeczy, z którymi nie czujesz się komfortowo, ranisz tylko swój kod teraz i siebie w przyszłości. Nie możesz rosnąć, pozostając w strefie komfortu. Musisz nauczyć się, jak mini maxpracować.

Agent_L
źródło
-1

Używanie Min, Max do podkręcania i cofania jest w porządku i nie jest sprzeczne z intuicją. Odbywa się to również przy użyciu:

  • floor () i sufit ()
  • zacisk, limit, granice
  • i oczywiście mod z obcięciem wysokiego rzędu.

W oprogramowaniu sprzętowym sięga dalej niż MMX, który sam wyprzedza nowoczesną grafikę 3D, która opiera się na tym rozszerzeniu.

Zastąpienie funkcji standardowej branży nawet lokalnie martwi mnie, pochodna nazwa może być lepsza. Być może studenci C ++ mogą przeciążać swoją mało znaną klasę.

mckenzm
źródło
Przepraszam, co jest z MMX?
Tobia Tesan,
Myślałem o „wartościach nasyconych”, w których wynik jest ograniczony. Pomysł polegał na tym, że kod mógłby być prostszy, gdyby nie musiał zawierać testów granicznych i przepełnienia (w grafice, aby uniknąć zmiany lub przejścia w dodatnią wartość, przekraczając granice ujemne). PSUBSB / PSUBSW. Przyznaję, że mechanizm może nie być taki sam, ale efekt i przeznaczenie funkcji jest.
mckenzm
-1

Jest w porządku, w niektórych przypadkach, ale nie w swoim przykładzie, ponieważ istnieje wiele lepszych sposobów wyrazu to:
saturate, clamp, clip, itd.

Mehrdad
źródło
-1

Wolałbym stworzyć ogólną funkcję o nazwie „ograniczony”

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

lub przy użyciu „min” i „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
źródło
-1

Co z wywoływaniem funkcji:

atmost(x,255): zwraca niższą z wartości x lub 255 co najwyżej.

atleast(10,x): zwraca wyższą z x lub co najmniej 10.

Sitowie
źródło
-1

min(x+y, MAX_VALUE); miałoby znacznie więcej znaczenia niż myCustomFunction(x, y);

Odpowiedź brzmi TAK, jest niewskazana . Służy jedynie jako alias języka twojego mózgu.

Iskra
źródło