1 / 19
Jan 2018

Swego czasu, w wątku --> 568. Zabawne dodawanie Piotrusia @j4rooo, wkleił poniższy fragment kodu:

int get_backward(int a){
	int result = 0;
	while(a) {
		result *= 10;
		result += a % 10;
		a /= 10;
	}
	return result;
}

Napisałem tam: "Kod @j4rooo też nie jest doskonały i kiedyś w wolnej chwili wstawię [myślę] że odrobinę lepszy mój :wink: "
No i nadszedł taki moment.

  1. Nazwa funkcji, powinna być opisowa, ale opisywać co a nie jak. Więc przedrostek get_, jest tu zbędny. Jeżeli myślisz inaczej, powinieneś natychmiast pozmieniać nazwy wszystkich funkcji w biblitekach, na np: get_sin, get_cos itd. Przedrostki get i set są w programowaniu obiektowym [w klasach] zarezerwowane do getterów i setterów, a backward to przecież “zwykła” funkcja, czyż nie?
  2. Funkcja [backward] jest w zasadzei doskonała, i spełnia swoje zadanie, jeżel jest nim uzyskanie AC. Ale przecież tu chodzi o coś więcej niż tylko AC. Chodzi o naukę i doskonalenie. Dlatego, poniżej kolejne [2] moje wersje, bez zbędnego komentowania:
int backward (int number, int base = 10){
    int result = 0;
    do
        result = result * base + number % base;
    while (number /= base);
    
    return result;
}

A co, gdy chcielibyśmy mieć taką samą funkcję dla liczb typu long long lub unsigned? W języky C, zastosowałbyś pewnie przeciążanie nazwy funkcji, ale w C++ możesz , a nawet powinieneś wykorzystać szablon [template]

template <class Number>
Number backward (Number number, int base = 10){
    Number result = 0;
    do
        result = result * base + number % base;
    while (number /= base);
    
    return result;
}

Szczęśliwego Nowego 2018 Roku! :wink:

  • created

    Dec '17
  • last reply

    Jan '18
  • 18

    replies

  • 2.8k

    views

  • 5

    users

  • 5

    likes

  • 15

    links

Nie wim czy widzisz różnicę, pomiędzy ładowaniem wszystkiego w jedną linię, a moją skromną propozycją, ale oczywiście masz rację [jeżeli będzie to faktycznie wszystko]. Jeżeli zauważyłeś tylko to jedną zmianę, to zostawię to bez komentarza.
Poniżej, specjalnie dla Ciebie, wersja z kilkoma liniami :wink:

template <class Number>
Number backward (Number number, int base = 10){
    Number result = 0;
    do {
        result *= base;
        result += number % base;
    } while (number /= base);
    
    return result;
}

Chcesz moją szczerą opinię? Z językiem programowania jest trochę jak z językiem mówionym - większość ma rację. Kod bardzo stracił na czytelności, oto dlaczego:

  • pętla do while już dawno przeszła do lamusa.
  • tak samo z klamrą po do ona musi być w kodzie, który mają czytać inni nawet jeśli sam wiesz, że jest zbędna. Tak można pisać w Pythonie.
  • używasz porównania do funkcji matematycznych, ale obrócenie liczby nie ma sensu matematycznego. Co prawda ona przyjmuje i zwraca liczbę ale, jest ona bardziej funkcją podobną do tych, które są używane na znakach/napisach jak, np. tolower. Zwróć uwagę na to w nazwie funkcji. Zgadzam się natomiast z tym, że to get można się pozbyć, ale backward nie może być nawą funkcji, bo jest przymiotnikiem/przysłówkiem. Chyba, że ktoś jest w stanie podać kontrargument? Dlatego w ogóle pojawiło się tam to get. Powinna się ona nazywać więc nazywać reverse (nawet jest już taka funkcja w std, ale obraca kontenery).
    No to ja również:

Szczęśliwego Nowego 2018 Roku!

Jak na mnie taki kod można traktować jako swojego rodzaju ciekawostkę - wszak nikt nie mówił, że ma być używana poza SPOJem :wink:

Skoro ciekawostka to wrzucę jeszcze jedną implementację, krótka i czytelna choć prawdopodobnie odrobinę wolniejsza :slight_smile: C++11:

#include <algorithm>

int reverse(int a) {
    string s = to_string(a);
    reverse(s.begin(), s.end());
    return stoi(s);
}

Mam takie dziwne wrażenie, że nie “wzieliście” z mojego postu tego co było w nim najcenniejsze, a przyczepiliście się do mało istotnych szczegółów. No i moją intencją nie było traktowanie mojego kawałka kodu jako jakiejś ciekawostki, czy tylko teoretycznego wywodu ale kawałka przydatnego przykładu kodu. Przecież akurat to co wyciągneliście, nie miało [zupełnie] aż takiego znaczenia, W takim razie, bardzo niechętnie [bo miał to być tutorial i poradnik dobrego programowania, a nie kolejny przykład [bezsensownego?] dyskutowania a p o mało istotnych sprawach.

Ta pętla była zawsze mniej wykorzystywana, ale jeżeli tak twierdzisz, to widocznie wiesz co mówisz. Oczywiście chodziło Ci o pętlę do { } while ();
W takim razie co z instrukcją goto? Wszyscy wiemy, że jest zła ale czy też przeszła do lamusa. Po prostu trzeba wiedzieć co się robi i tyle i o tym pisali już dawno autoryteci, ale może ja też nie wiem, że już odeszła do lamusa?. Mógłbym powiedzieć, że “goła” pętla while () { }, też może sobie iść do lamusa i mógłbym zamiast np:
while (n–) {
zacząć zawsze używać:
for (; n–; ) {
ale jak na razie tak nie zrobię i tam gdzie będę uważał, że [lamusowa] pętla do while jest lepsza, będę jej używał i jeżeli jakiekolwiek nawiasy będę uważał za zbędnę to nie będę ich wstawiał na siłe. Przecież nie mogę uważać i zakładać z góry, ewentualnego czytelnika mojego kodu, za aż tak bardzo mniej inteligentnego niż ja.

Zrobiłem to na szybko, ale ok, może w takim razie chociaż takie przykłady:
get_sort(), get_next_permutation itd
Faktycznie nazwa funkcji powinna być czasownikiem i od razu miałem wątpliwości co do nazwy backward pozbawionej przedrostka get, ale tu zawiodła moja słaba [nie wystarczająca] znajomość angielskiegi i świadomość, że czasownik reverse jest już wykorzystany w STL.

Z drugiej strony, sin, cos itd są nazwami funkcji matematycznych, więc są chyba rzeczownikami? ale to już inna bajka no i mogę się jednak mylić.

Nazwy funkcji to już w dużej mierze kwestia stylu - własnego albo narzuconego. Na SPOJu bym się tego nie czepiał bo konsystentne nazewnictwo nie jest tu kluczowe.

Odkąd siedzę w świecie polskich przepisów i interpretacji prawnych… tam to dopiero jest wesoło :wink: Ta wesołość przechodzi potem na wszystko inne i robi się mniej wesoło.

Takie tam fragmenty kodu:

FUNCTION tnijZnaczniki(txt) //Czyli w naszym kraju funkcje muszą być patriotyczne...
  	LOCAL cEndTag //...ale też bez przesady - inglisz is kul ;)

       //BLA BLA
  	WHILE (left(txt,1) == "<") //Ok! Nazwy funkcji wbudowanych będą z małej litery...
    		ns := At(" ",txt) //...albo nie ;)

                //BLA BLA
  	ENDDO    

//Gdzies tam dalej np. local lCzyJestOOV

RETURN txt

FUNCTION putRow(p) //Ooo... inglisz!
	LOCAL i

  	APPEND BLANK //Stosujemy rozkazy i mieszamy z funkcjami. Na ogół upraszcza to kod... ale czy zawsze?

        //BLA BLA
RETURN 

A wspominałem, że w sumie to function = func = procedure = proc = … ? :wink: Powiedzmy, że niektóre języki programowania wspierają bałaganiarstwo, a czy coś stanowi nieład czy nieład artystyczny - o tym trudniej zdecydować.

Za najcenniejsze w Twoim poście uważam piękne zademonstrowanie, jak ładnie można wykorzystać funkcjonalności C++ typu argumenty domyślne albo templaty (szacun!). Algorytmicznie ciekawe jest też zmienienie lokalizacji porównań, o czym szczerze mówiąc raczej bym nie pomyślał z powodu czytelności kodu :wink:

PS
Teraz zauważyłem, że w tym (moim) kodzie nie zastosowano notacji węgierskiej do opisu argumentów funkcji. Funkcje proste więc łatwo je przeczytać, ale - znowu - o dobrych nawykach zapomina się przy chaotycznym kodowaniu, nadmiarze obowiązków i braku rąk do klepania.

Akurat to [jak i nieokreślona ilość argumentów] było już w c o ile mnie pamięć nie myli. Natomiast template, [dzięki] jak najbardziej są w C++ i można i należy ćwiczyć i używać i nawet na SPOJ’u

Jeżeli dla kogoś kluczowe jest uzyskanie AC [też tak miałem do niedawna i jeszcze co nieco z tego pozostało we mnie] to w tym wątku nie ma dla niego nic ciekawego i kluczowego :wink: .

Rzeczywiście czytelna, krótka, bardzo ciekawa i oczywiście, sorry @j4rooo, ale nie działająca. Powinieneś nie dołączać includa, a pozostawić czytelnikowi roztrzygnięcie czego brakuje i co musi dodać :wink: Może dobrym pomysłem, jest umieszczanie tu tylko samej “gołej” funkcji i link do ideone z działającym przykładem? W kawałku który zamieściłeś zabrakło inkludów i using namespace … lub użycie wszędzie gdzie trzeba przedrostka
std:: a najlepiej jak proponowałem wyżej [link do ideone].

Z jednej strony twój kawełek kodu to mała perełka, bo początkujący będzie [może] zastanawiał się jak to możliwe, że wewnątrz funkcji użyłeś tej samej nazwy [reverse]. Początkujący koder pomyśli [może], to przecież rekurencja :wink: Doświadczony user pomyśli, geniusz, wykorzystał przeciążanie funkcji. :wink:

BTW
Zaznaczyłeś, że ten kawałek kodu to C++11, ale …
C++11 to już lamus :wink: niedługo będzie C++17
użyłeś stoi, i ograniczyłeś się tylko do typu int a zobacz na stronie: http://www.cplusplus.com/reference/string/stoi/3 są jeszcze funkcje strtol i strlul, i zamiast int a może lepiej użyć szablonu - ale …

BTW
s.begin(), s.end() to może jeszcze nie lamus, ale źródła [np Clean C++ … 17 --> https://www.amazon.com/Clean-Sustainable-Software-Development-Practices/dp/14842279212] zalecają używać w poniższym wypadku:

reverse (begin(s), end(s));
zamiast 
reverse(s.begin(), s.end());


Ale wszystko się dewaluje i powoli przechodzi do lamusa. Wystarczy pobawiś się kodami ze strony Rafała Nowaka [wiki] i okaże się że paru już się nie da skompilować, trzeba wprowadzić bardzo drobne poprawki. Podobnie z kodami naszego guru, Stańczyka. Tam też już jakiś czas temu, aby skompilować jakiś kawałek kodu, musiałem się trochę nagłówkować i coś tam poprawić.
No i @tarpauwatratar, zamiast, jak ostatnio gdzieś napisałeś:
#define LL long long
lepiej użyj
typedef long long LL;
:wink:

#define to makra i polecenia preprocesora - pozostałość [spuścizna] po C, a chyba wiesz [pisałem na starym forum] co o tym myślę i oczywiście wszelkie źródła, chociażby w/w? :wink:

PS
Możes stwierdzić, że po prostu zamienisz
int reverse (int a) { …
na
unsigned long long (unsigned long long a) { …
proszę bardzo :wink: ale tu nie o to chodzi.

W porządnych kodach tak robię. Gdy używam define w taki sposób to znaczy, że chcę jak najszybciej uzyskać działający kod, a jego czytelność nie ma znaczenia :wink:

Poza tym jestem przeciwnikiem stosowania zbyt wielu skrótów myślowych w kodzie, zarówno na poziomie techniki programowania jak i na poziomie samego nazewnictwa. Choć chyba sam popadam w skrajność:

if(::aoWezly[nIndeksWezlaTworzacego]:get_przodek() != 0 .and. !::aoWezly[::aoWezly[nIndeksWezlaTworzacego]:get_przodek()]:czy_istnieje_syn(nLudzka);
  .and. nLudzka >= (1 - POPRAWKA_PROCENTOWA) * ::aoWezly[nIndeksWezlaTworzacego]:get_wartosc() .and. nLudzka <= (1 + POPRAWKA_PROCENTOWA) * ::aoWezly[nIndeksWezlaTworzacego]:get_wartosc();
  .and. nLudzka > ::aoWezly[::aoWezly[nIndeksWezlaTworzacego]:get_przodek()]:get_wartosc())

Zwracam uwagę, na stosowanie przeze mnie w Harbourze: zaszłości z C++ (if, a nie If, iF albo stosowany przez wielu IF), dość rozbudowanej notacji węgierskiej (nie tylko n - numeric, niewidoczny w przykładzie c - character, a - array, ale nawet ao - array of objects czy an - array of numerics), geterów i seterów w klasach, funkcji zapisywanych jako aaa_bbb_ccc(), zmiennych zapisywanych jako AaaBbbCcc, nader rozbudowanych (ale za to zrozumiałych i utrudniających pomyłkę w złożonych przypadkach, a to fragment kodu złożonego drzewa) nazw zmiennych i rzeczy utworzonych poprzez #define (czasami trzeba :wink: ) zapisywanych jako AAA_BBB_CCC

Kody, które się już nie kompilują to klasyka. Jest to duża przeszkoda dla osób początkujących, które są wówczas zmuszane do studiowania różnych technikaliów przekraczających ich aktualne możliwości, na czym wielu się wykłada. Dla osób mających wyczucie i choć blade pojęcie o tym co robią przestaje to być problemem - potrafią szukać i kombinować. Co trwa, ale jest rozwijające :wink:

Ja też pisałem ją na szybko za pierwszym razem, a też się przyczepiłeś, więc jesteśmy kwita :slight_smile:

Twoja też nie działa jak ją tylko wkleić na ideone 2:2 :smiley:

Kod działa od w górę C++11, bo wtedy został rozbudowany język daną funkcjonalność. To jest normalna konwencja, która nie wymaga zbędnego komentarza i używana powszechnie np. na stackoverflow: https://stackoverflow.com/questions/4668760/converting-an-int-to-stdstring/46687995

Poza moimi uwagami przkład jest ciekawy. Ale ten, który podałeś dalej jest lepszy, mimo że dłuższy. Nie chodzi żeby ten kod napisać jak najkrócej, jak np. tutaj: http://pl.spoj.com/problems/MBREV/4 :slight_smile:

Jarek, wiesz, że Cię lubię i cenię i nie chcę rywalizować z tobą na punkty i nic tego nie zmieni [mam nadzieję] ale tu już przeginasz :wink: Przecież w tamtym zadaniu masz 8 pkt więc wiesz doskonale, że tam trzeba trochę więcej niż tylko bezmyślne skracanie kodu, aby uzyskać ten wynik [8]- chociaż wcale nie jestem dumny z tamtego zadania. Gdyby tutaj chodziło mi o to samo - tylko i wyłącznie skracanie kodu, to nie koniecznie użyłbym template, a zmienne nazwałbym jednoliterowo, no i nie zostawiłbym [specjalnie] pustej linii, przed returnem [taka strata ;-)] Dla Ciebie jest naturalne pisanie w taki a dla mnie w inny sposób.

Okazuje się, że jednak do-while nie odeszło do lamusa. Bjarne “tylko” unika [avoid] jej stosowania jako potencjalnego źródła błedów oraz chce mieć już na początku pętli widoczny warunek itd. A już się bałem, że też muszę odejść do lamusa razem z do-while i goto, chociaż i tak to nieuniknione, a co ma wisieć nie utonie :wink:

Takie dwa, bardzo proste, przykłady:

do
   cout << n % 10;
while  (n /= 10);
while (n) 
{  // taki standart też istnieje ;-)
  cout << n % 10;
  n /= 10;
}

Oba działają mam nadzieję, po drobnych uzupełnieniach i wcale nie chodzi mi, która wersja jest krótsza a która dłuższa. Działają doskonale dla n > 0. Co jednak, gdy chcemy aby programik[i] działały też dla n >= 0, a konkretnie też dla n = 0? Możliwe, że ja po prostu nie widzę prostszego rozwiązania od do-while, bo while (n >= 0){ oczywiście jest poważnym błędem :wink: Można oczywiście dodać dodatkowego if’a i else, ale miało być prościej i ewentualnie ładniej od “gołej” pętli do-while, który w tym wypadku sprawdza się doskonale.
Z oczywistych względów nie objaśniam, czego oczekuję od tych programików - co mają za zadanie robić.

Ludziska, nie pobijcie się o sposób pisania pętli :wink: Na tym świecie są na prawdę ciekawsze problemy, o które dużo bardziej warto się bić :wink:

ta dyskusja coś mi przypomina:

a ja mam wiaderko w ładniejszym kolorze i większą łopatkę :slight_smile:

To nie moja wina, to raczej chyba jednak Wasza? :wink: Ale jednak przypomnę [bardzo niechętnie] :wink:

Przeczytajcie może po prostu, od początku i uważne i wyciągnijcie jeżeli znajdziecie coś ciekawego i pożytecznego.

Mam wrażenie, że nie zwróciliście uwagi na to, że na spoj-u są obecnie widoczne dwie grupy użytkowników:

  1. początkujący - dla nich wasza dyskusja jest tak samo ciekawa i zrozumiała, jak “Sztuka wojny” Sun Tzu, oczywiście w oryginale :slight_smile: (bo już tłumaczenie zainteresowały by ich bardziej)

  2. zaawansowani, o ustalonych poglądach na programowanie, dla nich ta dyskusja to jedynie ciekawostka

Być może są na spoj-u średnio zaawansowani, ale na forum się nie ujawniają

Zawsze można wierzyć, że pierwsza grupa dzięki próbom zrozumienia wiedzy tajemnej stanie się drugą grupą :wink:

Średnio zaawansowani to krótki stan przejściowy realizowany głównie poza SPOJem (na starym forum był taki etap znajomości SPOJa by @narbej - użytkownik poznaje wady SPOJa). Osoby na tym etapie wiedzą już, że SPOJ to nie wszystko i znikają się przepoczwarzyć robiąc coś poza portalem, a potem wracają już jako zaawansowani :wink:

No właśnie ich staram się naprowadzić na właściwe tory, bo pamiętam jak kiedyś te rzeczy sprawiały mi jakieś problemy, których mógłbym uniknąć. Przemilczę całą resztę, którą mógłbym to jeszcze napisać.

Suggested Topics

Want to read more? Browse other topics in Tutoriale, poradniki or view latest topics.