Który kod jest bardziej czytelny

0

Oto pytanie. Który kod jest bardziej czytelny Waszym zdaniem.

KOD1:

char *rev(char *str)
  {
   char *p=str,*q=p;
   while(*q) ++q;
   while(p<--q) { char ch(*p); *p++=*q; *q=ch; }
   return str;
  }

KOD2:

char *rev(char *str) {
  const std::size_t length = std::strlen(str);
  for (std::size_t i = 0; i < length / 2; ++i) {
    std::swap(str[i], str[length - i - 1]);
  } 
  return str;
}

Jak widać oba robią to samo. Oba są stąd: http://4programmers.net/Forum/C_i_C++/215669-funkcja_zwracajaca_napis_nie_dziala

0

Eh, szkoda, że nie ma opcji zmiany wyboru, bo się machnąłem.

5

Gdyby drugi miał klamerki po ludzku i "using namespace std", to głosowałabym na 2.

2

Po drobnych modyfikacjach:

char *rev(char *str) 
{
  const size_t length = strlen(str);
  for (size_t i = 0; i < length / 2; ++i) 
       swap(str[i], str[length - i - 1]);
   
  return str;
}

Chyba nikt nie powie że pierwszy czytelniejszy. Można nie znać w ogóle C++ ani C i wiedzieć co ten kod robi. W pierwszym przypadku to by było raczej trudne. Tak w ogóle, to jestem przeciwnikiem stawiania jakichkolwiek klamerek w takim przypadku jak tutaj klamerki pod for.

1

Ech. To już jest kwestia pewnych konwencji samego formatowania w tym konkretnym kodzie - jak daje się klamry i jakie wcięcia się robi. Zupełnie nie o to pytałem. Proszę wybrać z tamtych dwóch takich, jakie są teraz i jakie formatowanie mają teraz. To nie ma być konkurs kto teraz napisze czytelniej, tylko chciałbym wiedziec których z tamtych dwóch robiących to samo jest bardziej czytelny Waszym zdaniem. Nie pytam przecież tylko o formatowanie.

Edit: Ahahahha, tutaj się dostaje minusa za doprecyzowanie o co chodzi we własnym wątku. :-D Paranoja. Błagam, niech mi ten ktoś napisze jakoś anonimowo za co ten minus, jestem tak nieprawdopodobnie ciekawy co kieruje takimi ludźmi, że nie da się tego opisać. Mówię poważnie.

0

Ja, niesiedzący w C++, bardziej zrozumiem drugie, ale dla haksiorów pewnie będzie to zbyt rozlazłe.

1

const std::size_t length = std::strlen(str);

Te std:: wszędzie zaciemniają kod.
Dlaczego?
Dlatego, że przy czytaniu tego kodu, trzeba mentalnie je pomijać i udawać że tego nie ma.

Ale tak poza tym, to drugi (w wersji @othello) jest czytelniejszy.

0

KOD2 - z jednego powodu na pewno:

p<--q

Ogólnie unikam arytmetyki na wskaźnikach, ale to już dla mnie przegięcie. Co to jest, typografika?
"Don't make me think!"

1

Autorzy obu funkcji powinni dostać kopa w dupę, bo skoro funkcja zwraca argument to intuicja podpowiada, że otrzymamy nową wartość, a tymczasem obie funkcje modyfikują argument!

0

GoingNative 2012 - Bjarne Stroustrup
Zdecydowanie drugi. Jeżeli już rozmawiamy o wcięciach, to też takie wole. Chociaż do swojego kodu nie użyłbym ani jeden ani drugiej funkcji.

0

Dla oba zapisy są równie czytelne. Co prawda w pierwszym zastosowałbym trochę pełniejsze znaczeniowo nazewnictwo, ale w tym konkretnym przypadku nie wpływa to za mocno na nieczytelność kodu.

0

drugi jest czytelniejszy, ale pierwszy ładniejszy

@Endrju
Co tu dużo wyjaśniać. Lubię kod, który wygląda tajemniczo, skomplikowanie czy wykorzystuje niekonwencjonalne rozwiązania. To mnie kręci.

4
Sopelek napisał(a):

drugi jest czytelniejszy, ale pierwszy ładniejszy
@Endrju
Co tu dużo wyjaśniać. Lubię kod, który wygląda tajemniczo, skomplikowanie czy wykorzystuje niekonwencjonalne rozwiązania. To mnie kręci.

A pracujesz zawodowo? Bo jak ja widzę taki haxiorski kod, i to jeszcze wpleciony w funkcje linq (c#) na trzech poziomach abstrakcji zahaczające o otchłań, to mnie krew zalewa. Debugowanie czegoś takiego to istny koszmar. Kod (no przynajmniej biznesowy) nie ma kręcić. Ma być dobrze i przejrzyście napisany - zasada KISS się kłania.

Mnie bardziej kręci jak ktoś napisze logicznie spójny i rozszerzalny kod, a nie od razu zobfuskowany

0

Przy aplikacjach biznesowych kod powinien w możliwie najprostszy sposób odwzorowywać problem domeny, aby być zrozumiały dla wszystkich uczestników projektu (oraz tych potencjalnych, którzy mogą dołączyć w trakcie trwania projektu). Takie nieintuicyjne zapisy są więc dobre hobbistycznie w domu, lub na konkursach obfuskacji.

2

Oba wyglądają jak po obfuskacji, ale w drugim idzie szybko domyślić się o co chodzi. Jednak to, co robi funkcja ma być zawarte w jej nazwie, więc jeśli kodu jest mało, a nazwa dobra, to jak dla mnie może być tam i assembler.

0

Ze wzgledu na kontekts w jakim zostaly umieszczone oba kody (w temacie zrodlowym) wybralem KOD2:

  • bez watpienia bardziej przejrzysty dla osoby poczatkujacej / nie znajacej jezyka
  • wykorzystujacy "dobrodziejstwa" C++
  • bardziej naturalny
  • mniej operacji na pointerach (=zazwyczaj mnie bledow)
  • nie moj styl formatowania :)

Co do kodu 1, ktory mi sie osobiscie bardziej podoba jest on napisany bardziej C-style i niestety niezbyt nadaje sie on w temacie, gdzie osoba pyta o podstawy podstaw.

0

A ja się głupio zapytam co oznacza taki zapis:

ch(*p) 

Czemu on jest równoważny?

Drugie głupie pytanie czy zamiast

while(p<--q) 

nie powinno być:

while(p<q--) 

Intuicja podpowiada mi, że tak byłoby bardziej poprawnie ;-), ale pewności nie mam!

Natomiast jeśli początkujący również mogą się wypowiedzieć to jeśli chodzi o porównanie kodów to jeden i drugi ma swoje wady i zalety. Ten pierwszy na pierwszy rzut oka wygląda na napisany prosto, jednak jak bliżej się przyjrzeć to zaczynają się wątpliwości co tam w środku się dzieje ;). Ten drugi kod wymaga znajomości kilku użytych funkcji. Wywaliłbym również std::, które jak ktoś już wcześniej wspomniał trochę zaśmieca kod ;).
Natomiast zgodzę się z przedmówcami, że głównym celem jest to, by funkcja robiła to co do niej należy - a sposób jej zaimplementowania jest drugorzędny.
pozdrawiam

0
Ciekawy napisał(a):

Drugie głupie pytanie czy zamiast

while(p<--q) 

nie powinno być:

while(p<q--) 

Intuicja podpowiada mi, że tak byłoby bardziej poprawnie ;-), ale pewności nie mam!

Byłoby,albo i nie było.Polecam sobie poczytać,co to jest pre i post dekrmentacja.

W skrócie - w wariancie 1 q najpierw zostanie zmniejszone i porównane z p na podstawie czego podjęta zostanie decyzja czy wykonać ciało while'a,natomiast w 2 nastąpi najpierw porównanie 'starej' wartości q z p i wejście w ciało pętli ze zdekrementowaną wartością q

0

Kod1, z sympatii do c (:
Kod2 zbytnio zalatuje javą.

Gdyby tylko w kodzie1 były spacje tam gdzie trzeba, byłbym happy.

"q<--p" na pierwszy rzut oka wygląda jak długa strzałka w lewo :D

0

Ogólnie unikam arytmetyki na wskaźnikach, ale to już dla mnie przegięcie. Co to jest, typografika?
"Don't make me think!"

Albo operator strzałka ;P Kreatywniejszy zapis wygląda tak: p <-- q (istnieje też p --> q) i można czytać jako q dąży do p. Zabawniejsze użycie to np. pętla for w dół:

for (int i = container.size(); i --> 0; ) { ... }

Po potraktowaniu jako idiom ma większy sens...
...no dobra, też bym kodu z takim zapisem raczej nie wysłał na forum.

Odnośnie tematu, pierwszy kod po unormowaniu klamerek (kwestia gustu) i zamienieniu *q = p, while(*q) ++q; na q += strlen(str) byłby niezłym kodem w C. A po przeniesieniu zmiany wskaźników na koniec (ta <-- strzałka i *p++=*q) byłby najoczywistszym rozwiązaniem takiego problemu (w C).

PS. http://c2.com/cgi/wiki?ThreeStarProgrammer

3

Kilka osób wspomniało o wydajności. Otóż u mnie wygląda to tak:

rev1: h4xi0r
3404 ms
rev2: plain c++
2012 ms

(Gentoo Linux, GCC 4.7.2, -O2 -march=native, Phenom II 3.2 GHz)

Test wygląda tak: mam tablicę char *strings[count]. W każdym elemencie tablicy jest wskaźnik na napis o maksymalnej długości length znaków. Każdy taki napis ma losową długość i składa się z losowych liter. Każdy napis jest obracany za pomocą testowanej funkcji. To obracanie całości wykonywane jest wiele razy w pętli, aby dostać jakiś mierzalny czas.

Dołączam kod z testem, wystarczy make run. rev_test.zip Myślę, że aby wynik był jak najbardziej wiarygodny powinno się wyłączyć skalowanie częstotliwości procesora, przynajmniej ja tak robię. (Z włączonym skalowaniem rezultat jest u mnie taki sam - tylko czasy inne, ale kto to tam wie ;-)

Edit: wsadziłem oba testy do jednego programu, żeby nie było wątpliwości (wynik bez zmian).

0

Mógłby ktoś wytłumaczyć zasadę:

(ch = *p) == (ch(*p) )

??? Nie mogę znaleźć w google a k&r też nic nie ma.

0
seksi rms napisał(a):

Mógłby ktoś wytłumaczyć zasadę:

(ch = *p) == (ch(*p) )

??? Nie mogę znaleźć w google a k&r też nic nie ma.

Hę? To jest C++ a nie C (wiem, że nie wygląda). To składnia wywołania konstruktora, dla typów wbudowanych też tak można.

0
Endrju napisał(a):
seksi rms napisał(a):

Mógłby ktoś wytłumaczyć zasadę:

(ch = *p) == (ch(*p) )

??? Nie mogę znaleźć w google a k&r też nic nie ma.

Hę? To jest C++ a nie C (wiem, że nie wygląda). To składnia wywołania konstruktora, dla typów wbudowanych też tak można.

coś jak Konstruktor konwertujący? Gdzię mogę więcej o tym poczytać?

0

Prędzej konstruktor kopiujący, jako argument do konstruktora leci zwykła wartość typu char po deferencji wskaźnika.

1 użytkowników online, w tym zalogowanych: 0, gości: 1