Dlaczego instrukcje „if elif else” praktycznie nigdy nie mają formatu tabeli?

73
if   i>0 : return sqrt(i)  
elif i==0: return 0  
else     : return 1j * sqrt(-i)

VS

if i>0:  
   return sqrt(i)  
elif i==0:  
   return 0  
else:  
   return 1j * sqrt(-i)  

Biorąc pod uwagę powyższe przykłady, nie rozumiem, dlaczego właściwie nigdy nie widzę pierwszego stylu w bazach kodu. Dla mnie zmieniasz kod w format tabelaryczny, który wyraźnie pokazuje, czego chcesz. Pierwsza kolumna może być praktycznie zignorowana. Druga kolumna określa warunek, a trzecia kolumna zawiera oczekiwane dane wyjściowe. Wydaje mi się, przynajmniej prosty, łatwy do odczytania. Jednak zawsze widzę, że ten prosty rodzaj sprawy / przełącznika pojawia się w rozszerzonym, wciętym formacie. Dlaczego? Czy ludzie uważają, że drugi format jest bardziej czytelny?

Jedynym przypadkiem, gdy może to być problematyczne, jest zmiana kodu i wydłużenie go. W takim przypadku myślę, że całkowicie rozsądne jest przeformułowanie kodu na długi, wcięty format. Czy wszyscy robią to w drugą stronę, po prostu dlatego, że zawsze tak było? Będąc adwokatem diabła, sądzę, że innym powodem może być to, że ludzie znajdują dwa różne formaty w zależności od złożoności instrukcji if / else, które są mylące? Doceniony zostanie każdy wgląd.

horta
źródło
91
Ponieważ ludzie uważają drugą opcję za bardziej czytelną?
GrandmasterB
65
Przypadek użycia wzajemnie wykluczających się gałęzi, które zwracają wartość tego samego typu, nie pojawia się często w imperatywnych językach w porównaniu do gałęzi, które mogą nie zwracać wartości, mogą obejmować wiele wierszy i prawdopodobnie mieć skutki uboczne. Jeśli spojrzysz na funkcjonalne języki programowania, zobaczysz kod, który znacznie bardziej przypomina twój pierwszy przykład.
Doval,
47
@horta „Jedynym przypadkiem, w którym może to być problematyczne, jest zmiana kodu i wydłużenie go.” - NIGDY nie należy zakładać, że fragment kodu nie zostanie zmieniony. Refaktoryzacja kodu zajmuje znaczną większość cyklu życia oprogramowania.
Charles Addis
7
@horta: Nie ma ze mną nic wspólnego. To jest kod. W kontekście czytanej bazy kodu chcę sprawdzić, czy instrukcje (i inne konstrukcje językowe) są konsekwentnie formatowane, bez żadnych przypadków krawędzi. Nie chodzi o to, że nauczyłem się czytać kod w określony sposób, potrafię dobrze czytać, ale jest bardziej czytelny, jeśli wszystko jest takie samo. Znowu nie to samo dla mnie, to samo dla reszty kodu.
GManNickG
44
Ponadto większość debuggerów jest oparta na linii. Nie można umieścić punktu przerwania w instrukcji, która jest w środku, ifjeśli jest w tej samej linii.
isanae

Odpowiedzi:

93

Jednym z powodów może być to, że nie używasz języków, w których jest popularny.

Kilka kontrprzykładów:

Haskell ze strażnikami i wzorami:

sign x |  x >  0        =   1
       |  x == 0        =   0
       |  x <  0        =  -1

take  0     _           =  []
take  _     []          =  []
take  n     (x:xs)      =  x : take (n-1) xs

Erlang z wzorami:

insert(X,Set) ->
    case lists:member(X,Set) of
        true  -> Set;
        false -> [X|Set]
    end.

Emacs lisp:

(pcase (get-return-code x)
  (`success       (message "Done!"))
  (`would-block   (message "Sorry, can't do it now"))
  (`read-only     (message "The shmliblick is read-only"))
  (`access-denied (message "You do not have the needed rights"))
  (code           (message "Unknown return code %S" code)))

Generalnie widzę, że format tabeli jest dość popularny w językach funkcjonalnych (i ogólnie w oparciu o wyrażenia), podczas gdy łamanie linii jest najbardziej popularne w innych (głównie na podstawie instrukcji).

viraptor
źródło
10
Głosowałem za tym i ogólnie się zgadzam, ale czuję się zobowiązany do zaznaczenia, że ​​1. Wszystkie są trywialnymi zwrotami 2. Haskellery lubią komicznie krótkie identyfikatory oprócz zwięzłości samego języka i 3. Języki funkcjonalne zwykle oparte są na wyrażeniach , a nawet języki rozkazujące mają inne standardy wyrażeń niż wyrażenia. Jeśli OP przepisałby oryginalny przykład jako aplikacje funkcyjne, mógłby otrzymać inną radę ...
Jared Smith
3
@JaredSmith Dzięki za podział oparty na wyrażeniu / wyrażeniu - myślę, że może być jeszcze bardziej odpowiedni niż funkcjonalny / konieczny. Co więcej, ruby ​​jest prawie oparty na wyrażeniach i nie korzysta często z tej konwencji. (wyjątki od wszystkiego) Jeśli chodzi o punkty 1 i 2, uważam, że ponad 50% prawdziwego kodu Haskell to „trywialne zwroty”, które są tylko częścią czegoś większego - tak po prostu napisano ten kod - nie tylko w przykładach tutaj. Na przykład blisko połowa funkcji tutaj to jedna tylko jedna / dwie linijki. (niektóre linie używają układu tabeli)
viraptor
Tak. Nie jestem Haskellerem, ale robię trochę ocaml i okazuje się, że dopasowanie wzorca jest o wiele bardziej zwięzłe niż logicznie równoważne przełączniki, a funkcje polimorficzne i tak pokrywają wiele z tego gruntu. Wyobrażam sobie, że klasy typów Haskella rozszerzyłyby ten zasięg jeszcze bardziej.
Jared Smith
Myślę, że promuje to składnia przypadków wzorca. Ponieważ jest bardziej zwięzły i zwykle bliżej krótkiej skrzynki przełączników, łatwiej jest wyrazić ją jako jednowierszową. Często robię to również z krótkimi instrukcjami skrzynek z podobnych powodów. Jednak dosłowne if-elsestwierdzenia są zwykle rozłożone na wiele wierszy, chociaż nie są to proste trójki.
Isiah Meadows
@viraptor Technicznie pozostałe 50% kodu haskell to „nietrywialne zwroty”, ponieważ wszystkie funkcje haskell są funkcjonalnie czyste i nie mogą mieć skutków ubocznych. Nawet funkcje, które czytają i drukują do wiersza poleceń, są tylko długimi instrukcjami zwrotnymi.
Pharap
134

Jest bardziej czytelny. Kilka powodów, dla których:

  • Prawie każdy język używa tej składni (nie wszystkie, w większości - twój przykład wygląda jednak na Python)
  • isanae zwrócił uwagę w komentarzu, że większość debuggerów jest oparta na liniach (a nie na instrukcjach)
  • Zaczyna wyglądać jeszcze bardziej brzydko, jeśli trzeba wstawić średniki lub nawiasy klamrowe
  • Płynie od góry do dołu
  • Wygląda okropnie nieczytelnie, jeśli masz coś innego niż trywialne zwroty
    • Każda znacząca składnia wcięcia jest tracona podczas przeglądania kodu, ponieważ kod warunkowy nie jest już wizualnie oddzielony (od Dana Neely )
    • Będzie to szczególnie złe, jeśli będziesz kontynuować łatanie / dodawanie elementów do instrukcji 1-line if
  • Jest to czytelne tylko wtedy, gdy wszystkie twoje czeki są mniej więcej tej samej długości
  • Oznacza to, że nie można sformatować skomplikowanych instrukcji, jeśli instrukcje w instrukcjach wielowierszowych, będą musiały być onelinerami
  • O wiele bardziej prawdopodobne jest zauważenie błędów / logiki podczas czytania w pionie linia po linii, nie próbując parsować wielu linii razem
  • Nasz mózg odczytuje węższy, wyższy tekst DUŻO szybciej niż długi, poziomy tekst

Z chwilą, gdy spróbujesz to zrobić, ostatecznie przerobisz go na instrukcje wielowierszowe. Co oznacza, że ​​zmarnowałeś czas!

Również ludzie nieuchronnie dodają coś takiego:

if i>0:  
   print('foobar')
   return sqrt(i)  
elif i==0:  
   return 0  
else:  
   return 1j * sqrt(-i)  

Nie trzeba tego często robić, zanim zdecydujesz, że ten format jest znacznie lepszy niż alternatywa. Ach, ale można to wszystko wstawić w jednym wierszu! Enderland umiera w środku .

Albo to:

if   i>0 : return sqrt(i)  
elif i==0 and bar==0: return 0  
else     : return 1j * sqrt(-i)

Co jest naprawdę bardzo denerwujące. Nikt nie lubi formatować takich rzeczy.

I na koniec, rozpoczniesz świętą wojnę problemu „ile spacji dla zakładek”. To, co renderuje się doskonale na ekranie jako format tabeli, może nie renderować się na moim w zależności od ustawień.

Czytelność i tak nie powinna zależeć od ustawień IDE.

kraina krańca
źródło
14
@horta, ponieważ będziesz musiał go przekonwertować , jeśli sformatujesz go na początku w pierwszy sposób? Chodzi o zminimalizowanie pracy dla przyszłej krainy. Tworzenie całkiem białych tabel i zliczanie spacji i tabulatorów w celu zaktualizowania formatowania wizualnego, kiedy mogę dodać logikę do sprawdzania, czy sprawdzenie wcale nie jest fajne (ignorując implikacje dotyczące czytelności).
enderland
14
@horta Niekoniecznie mówi, że nie naprawi tego, mówi, że będzie musiał to naprawić, a to dużo czasu spędzonego na żmudnym formatowaniu, a nie na programowaniu.
Servy
11
@horta: IMHO twoja droga jest często mniej czytelna i zdecydowanie bardziej irytująca w formatowaniu. Można go również używać tylko wtedy, gdy „ifs” są małymi liniami, co jest rzadkim przypadkiem. Wreszcie, jakoś nie jest fajne, IMHO, nosić ze sobą dwa rodzaje „jeśli” formatowania.
dagnelies
9
@horta, wydajesz się być błogosławiony pracą w systemach, w których wymagania, systemy, interfejsy API i potrzeby użytkowników nigdy się nie zmieniają. Twoja szczęśliwa dusza.
enderland
11
Dodałbym: każda niewielka zmiana w jednym warunku może wymagać przeformatowania pozostałych w celu dopasowania do :pozycji -> robiąc różnicę w CVS, nagle staje się trudniej zrozumieć, co się właściwie zmienia. Dotyczy to również stanu vs ciała. Posiadanie ich w osobnych wierszach oznacza, że ​​jeśli zmienisz tylko jedną z nich, różnice wyraźnie pokażą, że zmienił się tylko warunek, a nie ciało.
Bakuriu
55

Jestem głęboko przekonany, że „kod jest czytany wiele razy, napisany kilka - więc czytelność jest bardzo ważna”.

Kluczową rzeczą, która pomaga mi, gdy czytam kod innych ludzi, jest przestrzeganie „normalnych” wzorców, które moje oczy są wyszkolone do rozpoznawania. Najłatwiej mogę odczytać wciętą formę, ponieważ widziałem ją tyle razy, że rejestruje się prawie automatycznie (przy niewielkim wysiłku poznawczym z mojej strony). Nie dlatego, że jest „ładniejszy” - to dlatego, że przestrzega konwencji, do których jestem przyzwyczajony. Konwencja bije „lepiej” ...

Art Swri
źródło
3
Ważne: thecodelesscode.com/case/94
Kevin
11
To wyjaśnia, dlaczego ludzie są konserwatywni. To nie wyjaśnia, dlaczego ludzie zdecydowali się napisać kod w określony sposób.
Jørgen Fogh
8
Pytanie brzmiało: „Dlaczego często to widzę”, a nie skąd ten styl. Oba pytania są interesujące; Próbowałem odpowiedzieć na ten, o którym myślałem, że został zapytany.
Art Swri,
16

Wraz z innymi już wspomnianymi wadami układ tabelaryczny zwiększa szanse na konflikty scalania kontroli wersji wymagające ręcznej interwencji.

Gdy blok wyrównywanego tabelarycznie kodu musi zostać wyrównany, system kontroli wersji potraktuje każdy z tych wierszy jako zmodyfikowany:

diff --git a/foo.rb b/foo.rb
index 40f7833..694d8fe 100644
--- a/foo.rb
+++ b/foo.rb
@@ -1,8 +1,8 @@
 class Foo

   def initialize(options)
-    @cached_metadata = options[:metadata]
-    @logger          = options[:logger]
+    @metadata = options[:metadata]
+    @logger   = options[:logger]
   end

 end

Załóżmy teraz, że w międzyczasie w innej gałęzi programista dodał nową linię do bloku wyrównanego kodu:

diff --git a/foo.rb b/foo.rb
index 40f7833..86648cb 100644
--- a/foo.rb
+++ b/foo.rb
@@ -3,6 +3,7 @@ class Foo
   def initialize(options)
     @cached_metadata = options[:metadata]
     @logger          = options[:logger]
+    @kittens         = options[:kittens]
   end

 end

Scalenie tego oddziału nie powiedzie się:

wayne@mercury:/tmp/foo$ git merge add_kittens
Auto-merging foo.rb
CONFLICT (content): Merge conflict in foo.rb
Automatic merge failed; fix conflicts and then commit the result.

Gdyby modyfikowany kod nie używał wyrównania tabelarycznego, scalanie przebiegałoby automatycznie.

(Ta odpowiedź była „plagiatem” z mojego własnego artykułu zniechęcającego do wyrównania tabelarycznego w kodzie).

Wayne Conrad
źródło
1
Ciekawe, ale czy nie jest to błąd narzędzi do scalania? Szczególnie git w tym przypadku? Jest to punkt danych na temat konwencji, który jest najłatwiejszą drogą. Dla mnie jest to coś, co można ulepszyć (od strony narzędzia).
horta
7
@horta Aby narzędzie scalające zmodyfikowało białe znaki w sposób, który nie zawsze łamie kod, będzie musiało zrozumieć, w jaki sposób może modyfikować białe znaki bez zmiany znaczenia kodu. Będzie musiał także zrozumieć, jakie konkretne wyrównanie tabelaryczne jest używane. Będzie to nie tylko zależne od języka (Python!), Ale prawdopodobnie będzie wymagać narzędzia do pewnego stopnia zrozumienia kodu. Z drugiej strony, łączenie liniowe można wykonać bez sztucznej inteligencji i dość często nawet nie psuje kodu.
Wayne Conrad
Gotcha Tak więc, jak wspomniano w innych komentarzach, dopóki nie będziemy mieć formatów wejściowych IDE lub programowania, które włączają tabele bezpośrednio do formatu, problemy z narzędziami zawsze będą istnieć, utrudniając życie tym, którzy wolą tabele.
horta
1
@horta Prawidłowe. Większość moich zastrzeżeń do wyrównania tabelarycznego w kodzie mogłaby odejść od wystarczająco zaawansowanych narzędzi.
Wayne Conrad
8

Formaty tabelaryczne mogą być bardzo ładne, jeśli rzeczy zawsze mieszczą się w przydzielonej szerokości. Jeśli jednak coś przekracza przydzieloną szerokość, często konieczne jest posiadanie części stołu, która nie jest wyrównana z resztą, lub dostosowanie układu wszystkiego innego w tabeli, aby pasował do długiego elementu .

Jeśli pliki źródłowe były edytowane przy użyciu programów zaprojektowanych do pracy z danymi w formacie tabelarycznym i mogących obsługiwać zbyt długie elementy przy użyciu mniejszego rozmiaru czcionki, dzieląc je na dwie linie w tej samej komórce itp., Wówczas warto zastosować tabelaryczne formatuje częściej, ale większość kompilatorów chce plików źródłowych wolnych od znaczników, które edytorzy musieliby przechowywać, aby zachować formatowanie. Używanie linii ze zmiennymi wcięciami, ale żaden inny układ nie jest tak przyjemny jak formatowanie tabelaryczne w najlepszym przypadku, ale nie powoduje prawie tyle problemów w najgorszym przypadku.

supercat
źródło
Prawdziwe. Zauważyłem, że edytor tekstu, którego używam (vim), ma straszne wsparcie dla formatowania tabelarycznego, a nawet szerokiego tekstu. Nie widziałem, żeby inne edytory tekstu radziły sobie znacznie lepiej.
horta
6

Istnieje instrukcja „switch”, która zapewnia tego rodzaju rzeczy w szczególnych przypadkach, ale myślę, że nie o to pytasz.

Widziałem, czy instrukcje w formacie tabeli, ale musi istnieć duża liczba warunków, aby było warto. 3 jeśli instrukcje najlepiej wyświetlać w tradycyjnym formacie, ale jeśli masz 20, to znacznie łatwiej jest wyświetlić je w dużym bloku, który jest sformatowany, aby był wyraźniejszy.

I jest sens: jasność. Jeśli ułatwia to dostrzeżenie (a twój pierwszy przykład nie jest łatwy do ustalenia, gdzie znajduje się: separator), sformatuj go odpowiednio do sytuacji. W przeciwnym razie trzymaj się tego, czego oczekują ludzie, ponieważ zawsze łatwiej to rozpoznać.

gbjbaanb
źródło
1
Wygląda na to, że OP używa Pythona, więc nie switch.
Jared Smith
2
„3, jeśli najlepiej wyświetlać wyciągi w tradycyjnym formacie, ale jeśli miałeś 20”… to masz większe problemy do przemyślenia! :)
Grimm Opiner
@GrimmTheOpiner Jeśli piszesz parser języka lub ciąg znaków AST, jest to bardzo możliwe rozwiązanie. Na przykład kiedyś przyczyniłem się do parsera JavaScript, w którym podzieliłem funkcję na 15-20 przypadków, po jednej dla każdego typu wyrażenia. Większość przypadków posegregowałem według własnych funkcji (dla znacznego wzmocnienia wydajności), ale długi czas switchbył koniecznością.
Isiah Meadows
@JaredSmith: Najwyraźniej switchjest zły, ale tworzenie słownika, a następnie wyszukiwanie go w celu wykonania trywialnych rozgałęzień, nie jest złe ...
Mark K Cowan
1
@MarkKCowan oh złapałem sarkazm, ale myślałem, że używasz go do kpienia ze mnie. brak kontekstu w Internecie i tak dalej.
Jared Smith
1

Jeśli Twoje wyrażenie jest naprawdę łatwe, większość języków programowania oferuje operator:: rozgałęziający:

return  ( i > 0  ) ? sqrt( i)
      : ( i == 0 ) ? 0
        /* else */ : 1j * sqrt( -i )

Jest to krótki czytelny format tabelaryczny. Ale ważną częścią jest: na pierwszy rzut oka widzę, czym jest „ważna” akcja. To jest oświadczenie zwrotne! O wartości decydują pewne warunki.

Jeśli z drugiej strony masz gałęzie, które wykonują inny kod, uważam, że wcięcie tych bloków jest o wiele bardziej czytelne. Ponieważ teraz istnieją różne „główne” działania w zależności od instrukcji if. W jednym przypadku rzucamy, w jednym logujemy się i zwracamy lub po prostu wracamy. Istnieje inny przepływ programu w zależności od logiki, więc bloki kodu zamykają różne gałęzie i sprawiają, że są one bardziej widoczne dla programisty (np. Szybkie odczytywanie funkcji w celu uchwycenia przepływu programu)

if ( i > 0 )
{
    throw new InvalidInputException(...);
}
else if ( i == 0 )
{
    return 0;
}
else
{
    log( "Calculating sqrt" );
    return sqrt( -i );
}
Falco
źródło
7
W rzeczywistości uważam, że „krótki, czytelny format tabelaryczny” jest koszmarem do przeczytania, podczas gdy format zaproponowany przez OP jest całkowicie w porządku.
Matteo Italia
@MatteoItalia co powiesz na tę edytowaną wersję?
Falco
5
Przepraszam, jeszcze gorzej; Myślę, że wynika to z faktu, że ?i :są trudniejsze do wykrycia niż if/ elsesłowa kluczowe i / lub z powodu dodatkowego „szumu” symboli.
Matteo Italia
@MatteoItalia: Miałem przypadki z ponad setką różnych wartości. Wartość tabeli umożliwia sprawdzenie błędów. W przypadku wielu linii jest to niemożliwe.
gnasher729,
1
@ gnasher729 - Dla 100 różnych „wartości” ogólnie stwierdziłem, że o wiele lepiej jest zadeklarować strukturę danych każdego „elementu” i wypisać to wszystko jako tabelę podczas inicjalizacji tablicy tych struktur danych. (Oczywiście mogą obowiązywać ograniczenia językowe). Jeśli jakikolwiek element wymaga aspektu „obliczeniowego”, struktura elementu może zawierać wskaźnik lub odwołanie do funkcji w celu wykonania potrzebnej akcji. W przypadku aplikacji majowych może to znacznie uprościć kod i znacznie ułatwić konserwację.
Michael Karas
1

Jak już powiedział Enderland, zakładasz, że masz tylko jeden „powrót” jako akcję i że możesz oznaczyć ten „powrót” na końcu warunku. Chciałbym podać dodatkowe szczegóły na temat tego, dlaczego to się nie powiedzie.

Nie wiem, jakie są twoje preferowane języki, ale pisałem w C od dłuższego czasu. Istnieje wiele standardów kodowania, których celem jest uniknięcie pewnych standardowych błędów kodowania poprzez niedopuszczenie konstrukcji kodu podatnych na błędy, zarówno podczas wstępnego kodowania, jak i podczas późniejszej konserwacji. Najbardziej znam MISRA-C, ale są też inne i ogólnie wszystkie mają podobne reguły, ponieważ rozwiązują te same problemy w tym samym języku.

Jednym z popularnych błędów, którym często zajmują się standardy kodowania, jest ten mały problem: -

if (x == 10)
    do_something();
    do_something_else();

To nie robi tego, co myślisz. Jeśli chodzi o C, jeśli x wynosi 10, wtedy wywołujesz do_something(), ale następnie do_something_else()zostaje wywołany niezależnie od wartości x . Tylko akcja bezpośrednio po instrukcji „if” jest warunkowa. Może to być zamierzenie kodera, w którym to przypadku istnieje potencjalna pułapka dla opiekunów; lub może nie być to zgodne z zamierzeniami kodera, w którym to przypadku wystąpił błąd. To popularne pytanie podczas wywiadu.

Rozwiązaniem w standardach kodowania jest nałożenie nawiasów klamrowych wokół wszystkich działań warunkowych, nawet jeśli są one jednowierszowe. Teraz dostaniemy

if (x == 10)
{
    do_something();
    do_something_else();
}

lub

if (x == 10)
{
    do_something();
}
do_something_else();

a teraz działa poprawnie i jest jasne dla opiekunów.

Zauważysz, że jest to całkowicie niezgodne z twoim formatem tabeli.

Niektóre inne języki (np. Python) przyjrzały się temu problemowi i zdecydowały, że ponieważ koderzy używają białych znaków do wyjaśnienia układu, dobrym pomysłem byłoby użycie białych znaków zamiast nawiasów klamrowych. W Pythonie

if x == 10:
    do_something()
    do_something_else()

uzależnia wywołania do obu do_something()i do_something_else()od x == 10, podczas gdy

if x == 10:
    do_something()
do_something_else()

oznacza, że ​​tylko do_something()zależy od x i do_something_else()zawsze jest wywoływane.

Jest to ważna koncepcja i znajdziesz ją w kilku językach. (Po raz pierwszy zobaczyłem to w Occam2, dawno temu.) Ponownie jednak możesz łatwo zauważyć, że twój format tabeli jest niezgodny z językiem.

Graham
źródło
1
Myślę, że przegapiłeś punkt. Problem, o którym mówisz, to dziwny, niestandardowy koszmar C, który powoduje problem, o którym mówisz. Jeśli koduję w C, nigdy nie zalecałbym użycia alternatywnej prostej metody if, jeśli zaproponowałem format tabelaryczny. Zamiast tego, ponieważ używasz C, używałbyś nawiasów klamrowych w jednym wierszu. Nawiasy klamrowe sprawiłyby, że format tabeli byłby jeszcze bardziej przejrzysty, ponieważ działają one jako ograniczniki.
horta
Również instrukcje return w tym przypadku są tylko przykładem. Ogólnie może to być sam w sobie zapach kodu. Mam na myśli jedynie format prostych instrukcji, niekoniecznie w ogóle instrukcji zwrotnych.
horta
2
Chodzi mi o to, że dzięki temu format tabeli jest jeszcze bardziej niezgrabny. Nawiasem mówiąc, nie jest on specyficzny dla C - jest wspólny dla wszystkich języków pochodzących z C, więc C ++, C #, Java i JavaScript pozwalają na to samo gotcha.
Graham
1
Nie mam nic przeciwko, że to zwroty - rozumiem, że twoim celem jest pokazanie prostych wyciągów. Ale staje się bardziej nieporęczne. Oczywiście, gdy tylko dowolna instrukcja stanie się niełatwa, musisz zmienić formatowanie, ponieważ format tabeli jest niemożliwy do utrzymania. O ile nie robisz zaciemnienia kodu, długie linie kodu same w sobie są zapachem. (Pierwotny limit wynosił 80 znaków, obecnie jest to zwykle około 130 znaków, ale ogólna zasada nadal mówi, że nie trzeba przewijać, aby zobaczyć koniec linii.)
Graham
1

Układ tabelaryczny może być użyteczny w kilku ograniczonych przypadkach, ale jest kilka razy, gdy jest to użyteczne w przypadku if.

W prostych przypadkach?: Może być lepszym wyborem. W średnich przypadkach zmiana jest często lepsza (jeśli Twój język ma taką opcję). W skomplikowanych przypadkach może się okazać, że lepiej sprawdzą się tabele połączeń.

Wiele razy przy refaktoryzacji kodu przestawiałem go na tabelaryczny, aby uczynić go oczywistym. Rzadko zdarza się, że zostawiam to w ten sposób, ponieważ w większości przypadków istnieje lepszy sposób na rozwiązanie problemu po jego zrozumieniu. Czasami zabrania się tego praktyka kodowania lub standard układu, w którym to przypadku przydatny jest komentarz.

Było kilka pytań na temat ?:. Tak, jest to operator trójskładnikowy (lub, jak lubię o tym myśleć, wartość if). przy pierwszym rumieniec ten przykład jest trochę skomplikowany?: (i nadużywanie?: nie poprawia czytelności, ale boli), ale z pewną myślą Przykład można zmienić w następujący sposób, ale myślę, że w tym przypadku przełącznik jest najbardziej czytelny rozwiązanie.

if i==0: return 0
return i>0?sqrt(i):(1j*sqrt(-i))
Hildred
źródło
1
Być może będziesz musiał wyjaśnić, czym jest „?:” Dla niewtajemniczonych (na przykład z przykładem, być może związanym z pytaniem).
Peter Mortensen
Zakładam, że to operator trójskładnikowy. Wydaje mi się, że należy tego unikać, ponieważ operator trójskładnikowy ma tendencję do przestawiania standardu, jeśli wykonuje różne czynności, a także inne formaty plików, które ludzie widzą każdego dnia i dlatego mogą je łatwo czytać.
horta
@PeterMortensen: Jeśli niewtajemniczeni nie wiedzą, co to znaczy, powinni trzymać się z dala od kodu, dopóki nie zadadzą oczywistego pytania i się nie dowiedzą.
gnasher729
@horta Ternary is if ? do stuff : do other stuff. Takie samo zamówienie jak w przypadku if / else.
Navin
1
@Navin Ah, może to tylko awaria języka, którego używam najczęściej (python). stackoverflow.com/questions/394809/…
horta
-3

Nie widzę nic złego w formacie tabeli. Osobiste preferencje, ale użyłbym trójki w następujący sposób:

return i>0  ? sqrt(i)       :
       i==0 ? 0             :
              1j * sqrt(-i)

Nie trzeba powtarzać za returnkażdym razem :)

Navin
źródło
1
Jak wspomniano w kilku komentarzach, zwroty nie są idealne ani nie stanowią sedna postu, po prostu fragment kodu, który znalazłem online i sformatowałem na kilka sposobów.
horta
Ternaries Python to do_something() if condition() else do_something_else()nie condition() ? do_something() : do_something_else().
Isiah Meadows
@IsiahMeadows OP nigdy nie wspominał o Pythonie.
Navin