Jakie znasz alternatywne struktury kontroli? [Zamknięte]


12

Podobne pytanie zostało zamknięte w sprawie SO.

Czasami podczas programowania stwierdzamy, że jakaś szczególna struktura sterowania byłaby dla nas bardzo przydatna, ale nie jest bezpośrednio dostępna w naszym języku programowania.

Jakie alternatywne struktury kontroli uważasz za użyteczny sposób organizowania obliczeń?

Celem jest tutaj znalezienie nowych sposobów myślenia o strukturze kodu, aby poprawić dzielenie na fragmenty i rozumowanie.

Możesz utworzyć składnię życzeniową / semantyczną, która jest obecnie niedostępna, lub przytoczyć mniej znaną strukturę sterowania w istniejącym języku programowania.

Odpowiedzi powinny dać pomysły na nowy język programowania lub ulepszenie rzeczywistego języka.

Pomyśl o tym jak o burzy mózgów, więc opublikuj coś, co Twoim zdaniem jest szalonym pomysłem, ale może być wykonalne w niektórych sytuacjach.

Chodzi o programowanie imperatywne.


1
Dlaczego to pytanie musi dotyczyć „programowania imperatywnego”?
missingfaktor

@missingfaktor: Ponieważ struktury sterowania dotyczą programowania imperatywnego. Pewnie, niektóre problemy można rozwiązać za pomocą podejścia funkcjonalnego lub w inny sposób, ale inne paradygmaty nie stawiają struktur kontrolnych w centrum rozwoju. Ale i tak możesz być kreatywny.
Maniero

3
Cóż, zamierzałem podzielić się dopasowaniem wzorców ze Strażnikami, ponieważ są one bardziej ogólne niż stwierdzenia Case i If-Thens, ale jeśli nie jest to strefa FP, to chyba więcej pasujących Wzorów dla reszty z nas!
CodexArcanum

@CodexArcanum: Cóż, Matching Pattern jest konstrukcją bardzo podobną do konstrukcji imperatywnych. Rzeczywiście, samo dopasowanie wzoru jest alternatywą dla imperatywnych struktur kontrolnych. Nie wstydź się ;-)
Maniero

Patrząc na wszystkie odpowiedzi, cieszę się, że żadna z propozycji nie istnieje w prawdziwym życiu (i mam nadzieję, że nigdy nie będzie). Przepraszam :)
serg

Odpowiedzi:


14

OK, to zabawne pytanie.

Chciałbym też mieć generała elsena chwilę i na pętle, bo gdy warunek nie jest spełniony przy pierwszym teście:

while (condition) {
    // process
}
else {
    // condition was never true
}

Pozwala to uniknąć niewygodnego ponownego obliczania warunku lub przechowywania go w zmiennej.


Python ma to.
Barry Brown

Hej! Wreszcie dziwny wybór Pythona, z którym się zgadzam! ;-)
Macneil

5
Nie. Klauzula else w pętlach for / while Pythona jest wykonywana, jeśli pętla kończy się normalnie, co oznacza, że ​​pętla nie jest przerywana przez instrukcję break lub return lub wyjątek. W pętlach dla klasy else wykonywana jest po wyczerpaniu sekwencji elementów, które są zapętlone, zaś w pętlach wykonywana jest po tym, jak warunek pętli po raz pierwszy oceni się na False.
pillmuncher

4
Pojawia się prośba o dodanie while ... elsekonstrukcji w sposób opisany przez Macneila w PHP. Myślę, że to świetny pomysł, ponieważ „oto wyniki / wyników nie było” jest dość powszechnym idiomem w aplikacjach internetowych.
Dean Harding

1
@SnOrfus: On chce bloku kodu (oddzielnego od bloku pętli), który będzie wykonywany TYLKO wtedy, gdy warunek while nigdy nie zostanie spełniony. Funkcja Do-While wykonuje blok kodu pętli (nie jakiś oddzielny blok) jeden raz, niezależnie od warunku while.
Legion

10

Dlaczego nie połączyć kilku odpowiedzi w jedną?

while (expr) {

    // Executed every iteration, unless first{} is present.
    // May be explicitly called rest{} if you like first{} to come first.

    // Blocks may return results, and consequently be used in expressions.
    return expr;

} first {

    // Executed only on the first iteration.

} pre {

    // Executed before every iteration.

} post {

    // Executed after every iteration.

} catch (oops) {

    // All blocks are implicitly try{}ed if followed by a catch{}.

} finally {

    // Executes after the block completes, regardless of exceptions.

} else {

    // Executed if the loop body or rest{} never executes.

} never {

    // Executes only when a client is present.

} drop (bad, worse), // Explicitly ignore certain exceptions.
  until (expr);      // Here, have a post-body condition, too.

Rozszerzalna, uogólniona składnia konstrukcji kontroli przepływu w języku imperatywnym byłaby raczej przydatna i zabawna. Dopóki się to nie pojawi, domyślam się, że użyję Lisp lub czegoś takiego.


5
Nieprzydatne, mylące. Bez rozrywki, powoli.
Josh K

2
@Josh K: Tak, masz rację. Myślałeś, że się nie zgodzę? Ta odpowiedź gryzie się w język, żeby się nie śmiać.
Jon Purdy,

Jeśli miałoby to być ironiczne, z pewnością by się udało!
Josh K

11
+1 Rozśmieszyło mnie. Potrzebuje klauzuli „between”, aby kod mógł być wykonywany między iteracjami.
Barry Brown

1
+1, ale potrzebuje seventh { ... }również.
j_random_hacker

7

Struktury sterowania jako funkcje.

Chcę for, if, else, while, etc być funkcje, nie specjalne konstrukcje.

Chcę return, try/excepti gotobyć pochodne kontynuacje.

Ma to oczywiście mniej wspólnego z określoną strukturą kontroli, a więcej z tym, jak postrzegasz struktury kontroli w ogóle, meta struktur kontroli.


2
Kontynuacje są ciężką koncepcją dla języka i istnieją dobre powody, aby pozostawić je poza wieloma językami.
David Thornley,

Nie zgadzam się; konieczności for, if, else, i whilejako funkcji sprawia mi wywnioskować, że parametry do tych funkcji należy leniwie wykonywane w celu zachowują się tak samo jak oryginalne konstrukcje. Byłoby miło wykonać leniwą egzekucję.
Dr Wily's Apprentice

1
@David: To, że tam są, nie oznacza, że ​​musisz ich używać. Zwykli programiści mogą korzystać z tego, do czego są przyzwyczajeni return, wyjątków itp. Ale teraz ekspert programista ma w swoim zestawie narzędzi potężne dodatkowe narzędzie, jeśli zajdzie taka potrzeba. Poza tym języki IMHO nie powinny być „głupie”. Języki powinny mieć możliwość wspierania zwiększonej wiedzy specjalistycznej i wzrostu wiedzy z zakresu CS.
dietbuddha

1
@dietbuddha: (kont.) Nie mówię, że kontynuacje są złe, mówię, że są ciężkie, a ich użyteczność ma wpływ na język, coś w rodzaju wyjątków. Ponadto kontynuacja może wymusić zmiany w sposobie korzystania z języka, podobnie jak wyjątki w C ++. Język, który obsługuje kontynuacje, będzie różny od języka, który nie działa, zarówno w dobry, jak i zły sposób.
David Thornley,

1
@ Steve314 - longjmp nie jest kontynuacją, chociaż istnieją podobieństwa. Kontynuacje zapisują cały stan (wszystkie lokalne, globalne i stos). longjmp zapisuje wskaźnik stosu, więc jeśli wyjdziesz poza zakres, w którym użyto setjmp, dostaniesz segfault, ponieważ ramka już nie istnieje. Uważam, że istnieją także ograniczenia dotyczące zapisywanych zmiennych.
dietbuddha,

6

Powiązany artykuł na pewno dobrze mówi o pętlach N + 1/2 Donalda Knutha . Wyrażony w C / C ++ / Java:

for (;;) {
  get next element;
  if (at the end) break;
  process the element;
}

Jest to przydatne do odczytywania wierszy lub znaków z pliku, sprawdzania, czy osiągnięto EOF, a następnie przetwarzania go. Jestem tak przyzwyczajony do tego, for(;;)..if(..)break;że pojawia się wzór, że jest to dla mnie idiomatyczne. (Zanim przeczytałem artykuł Knutha, przedrukowany w książce Literate Programming , ten kiedyś był „wtf?”.)

Knuth zasugerował słowa kluczowe loop/while/repeat:

loop:
  S;
while C:
  T;
repeat

Gdzie Si Tsą symbolami zastępczymi dla serii zer lub więcej instrukcji i Cjest to warunek logiczny. Gdyby nie było Sinstrukcji, byłaby to pętla while, a gdyby nie było Tinstrukcji, byłaby to pętla do.

Konstrukcję tę można uogólnić, dopuszczając zero lub więcej while Cklauzul, co czyni ją idealną do wyrażania nieskończonych pętli, a następnie niektórych rzadszych warunków, które wymagałyby dwóch kontroli.

W tym samym artykule Knuth zasugerował mechanizm sygnalizacyjny, który byłby lokalną wersją wyjątków rzucania / wyłapywania (jako alternatywa dla używania goto).

Dla mnie? Chciałbym, aby Java wspierała optymalizację połączeń ogonowych, aby w razie potrzeby móc wyrazić dowolną ogólną strukturę sterowania.


Aktualizacja: zapomniałem wspomnieć, że wielu programistów C / C ++ / Java obchodzi ten problem, używając wbudowanego przypisania pod warunkiem while:

while ((c = getc(f)) != -1) {
   T;
}

Używając terminów z konstruktu Knutha, jest to dopuszczalne, gdy Si Cmożna je połączyć w jedno wyrażenie. Niektórzy ludzie nienawidzą zobaczyć wbudowanego zadanie wyżej, inni nienawidzą, aby zobaczyć breakw for (;;)góry. Ale kiedy Si Cnie można go łączyć, na przykład gdy Sma wiele instrukcji, for (;;)jest to jedyna alternatywa bez powtarzania kodu. Inną alternatywą jest po prostu skopiowanie Skodu:

S;
while (C) {
  T;
  S;
}

loop/while/repeatAlternatywa Knutha wydaje się znacznie lepsza.


Ta pętla podczas powtarzania wygląda bardzo podobnie do repeat-untilpętli Pascala .
Mason Wheeler,

@Mason: Różnica polega na tym, że istnieją dwa miejsca, w których można wstawić instrukcje, a także warunek, a nie tylko jedno.
Macneil

Och, rozumiem co się dzieje. Tak, to interesujące ...
Mason Wheeler

ANSI Basic miał do while ... loop until- zarówno warunek wstępny, jak i końcowy są opcjonalne, a ja (niejasno) pamiętam używanie obu w jednej pętli. W twoim średnim stanie jest Ada exit when ...;. Kluczową zaletą if ... break;jest to, że możesz pisać, exit loopname when ...;aby wyjść z wielu (ale niekoniecznie wszystkich) zagnieżdżonych pętli jednocześnie. Prawdopodobnie również nieco bardziej widoczny niż ta przerwa.
Steve314,

Zabawna rzecz Ada ma dokładnie tę funkcję.
John R. Strohm,

6

Język BCPL miał valueofwyrażenie , którego można użyć do przekształcenia sekwencji instrukcji w jedno wyrażenie:

foo(a, b, valueof {some series of statements; resultis v});

Gdzie some series of statementsmoże być wszystko, a całość valueofocenia v.

Może to być przydatne w Javie, gdy trzeba obliczyć argument do wywołania a this()lub super()(co wymaga, aby nic się przed nim nie wydarzyło). Oczywiście możesz po prostu napisać osobną metodę, ale może to być uciążliwe, jeśli musisz podać wiele lokalnych wartości dla kontekstu.

Jeśli możesz użyć finalzmiennych, które są potrzebne, możesz już zrobić valueofw Javie za pomocą anonimowych klas wewnętrznych:

foo(a, b, new Object(){String valueof(){
    String v ...; some series of statements; return v;}}.valueof());

1
GCC ma do tego rozszerzenie - ({ statement1; statement2; ...; result-expr; }). Widziałem podobne gdzie indziej, ale nie pamiętam gdzie. Prawdopodobnie wszystkie skopiowane z BCPL.
Steve314,

6
unless(condition) {
  // ...
}

robi to samo co:

if(!condition) {
  // ...
}

repeat {
  // ...
} until(condition)

robi to samo co:

do {
  // ...
} while(!condition)

Możesz wybrać się do Lisp ...
duros

1
Lub Ruby, ma podobną składnię unless.
Josh K

2
Wygląda dość perlish. Jest na to więcej niż jeden sposób.

2
@ Steve314 Musisz użyć niepoprawnego stylu nawiasu klamrowego. ;-)
Orbling

1
@Orbling - wcale. Wszyscy inni używają niewłaściwego stylu nawiasów klamrowych.
Steve314,

5

Z drugiej strony chciałbym zobaczyć lepszą obsługę iteratorów w językach programowania. W szczególności, gdy chcesz zejść dwie kolekcje w parach :

for (String s, Integer i : stringsSet, integersSet) {
    // use the pair (s, i)
}

Niektóre dynamiczne języki mogą już to mieć lub łatwo obsługiwać za pomocą bibliotek i makr, ale myślę, że jest to zgodne z duchem twojego pytania.

Jeśli dwa zestawy nie są tego samego rozmiaru, może to spowodować wyjątek lub można użyć elsepętli po, aby zasygnalizować różnicę rozmiarów.

Oczywiście, możesz uogólnić to na zejście trzech lub więcej list.


Aktualizacja: Przydatne byłoby również wykonanie produktu kartezjańskiego między iteracjami:

for (String s, Integer i : stringsSet * integersSet) {
    // use the pair (s, i), each s with each i
}

co byłoby niczym więcej niż zagnieżdżonymi pętlami:

for (String s : stringsSet) {
    for (Integer i : integersSet) {
        // use the pair (s, i), each s with each i
    }
}

Jestem trochę zaniepokojony tym, że między dwoma zapisanymi tutaj notacjami istnieje różnica O (n) i O (n ^ 2) w liczbie par, z jedynie zmianą pojedynczego znaku.


2
W Pythonie są takie pliki zip i zip_longest.
pillmuncher

Fajnie, może nie doceniam Pythona i powinienem dać mu drugie spojrzenie po wielu latach. To mi przypomina, że ​​czasem potrzebujesz produktu kartezjańskiego, odpowiednika zagnieżdżonego dla pętli.
Macneil

1
Oba te przypadki w Scali: paste.pocoo.org/show/297429
missingfaktor

1
W odniesieniu do produktu kartezjańskiego: Znowu Python go ma. for a, b, c in itertools.product(iter1, iter2, iter3):daje leniwie oceniany produkt kartezjański. Co to jest? Chcesz także permutacji i kombinacji danego iteratora? itertools.permutations, itertools.combinations.
aaronasterling

1
Perspektywa Haskella: użyj „zipWith” dla pierwszego przypadku i zrozumienia listy lub monady List dla drugiej. Podobnie jak w przykładach Python / Scala, ale bardziej elegancki. :)
LennyProgrammers,

5

Istnieje tak zwana „pętla Dijkstry” (zwana również „strzeżoną pętlą Dijkstry”). Został zdefiniowany w The Guarded Command Language (GCL) . Informacje na temat jego składni i semantycznej można znaleźć w powyższym artykule w Wikipedii w sekcji 6 Powtórzenie: do .

Obecnie znam jeden język programowania, który bezpośrednio obsługuje tę strukturę sterowania. Jest to Oberon-07 (PDF, 70 KB). I obsługuje „Pętlę Dijkstry” w formie instrukcji while. Spójrz na rozdział 9.6. Chociaż oświadczenia w powyższym pliku PDF.

WHILE m > n DO m := m – n 
ELSIF n > m DO n := n – m 
END

PS To jest kopia mojej SO odpowiedzi .


Wygląda na to, że moduł sprawdzania modelu Spin ma również tę konstrukcję, z identyczną semantyką i zasadniczo identyczną składnią.
j_random_hacker

4

Wyrażenia w stylu ikon z wbudowanym śledzeniem.

Python ma wiele zalet generatora ikon - i ogólnie robi z nich lepszą robotę, IMO. I w zasadzie cofanie było tylko rodzajem wyjątku, ale prostota wyrażeń była z grubsza odpowiednikiem ...

x = (a / b) else c;

do obsługi przypadków awarii, takich jak dzielenie przez zero.

Gdzie Icon oszalał - brak operatorów porównujących zwracających wartość logiczną. Porównania zawsze albo się udawały, albo powodowały powrót do przeszłości, i istniała jeszcze inna kwestia semantyczna, którą desperacko próbuję sobie przypomnieć ... no powiedzmy, że jest to prawdopodobnie bardziej stłumione niż zapomniane.

Zawsze myślałem, że powinny mieć ifwyrażenie bez żadnej innej części - if (condition, success-value)coś w rodzaju cofania się, jeśli warunek zwraca wartość false - i porzucić dziwne porównania.

EDYCJA Pamiętam - naprawdę oczywiste. Porównanie z dwoma argumentami kończy się powodzeniem lub niepowodzeniem - nie oblicza nowej wartości do zwrócenia. Więc kiedy to się uda, co powoduje powrót? Odpowiedź - jeden z argumentów. Ale jeśli napiszesz a > b, jaki logiczny argument należy zwrócić - alub b? A jeśli b < azamiast tego napiszesz ? Myślę , że zawsze zwracał właściwy argument, co ma tak samo sens, jak cokolwiek innego, ale nadal zwykle wydawał mi się niewłaściwy argument.


4

To tylko ogólny pomysł i składnia:

if (cond)
   //do something
else (cond)
   //do something
also (cond)
   //do something
else
   //do something
end

TAKŻE warunek jest zawsze oceniany. ELSE działa jak zwykle.

Działa to również w przypadku. Prawdopodobnie jest to dobry sposób na wyeliminowanie instrukcji break:

case (exp)
   also (const)
      //do something
   else (const)
      //do something
   also (const)
      //do something
   else
      //do something
end

można odczytać jako:

switch (exp)
   case (const)
      //do something
   case (const)
      //do something
      break
   case (const)
      //do something
   default
      //do something
end

Nie wiem, czy jest to przydatne, czy łatwe do odczytania, ale to przykład.


3

Przychodzi mi na myśl kontynuacja Passing Style . W takim razie, oczywiście, chciałbyś również mieć Optymalizację ogona .


1
Nie jestem wielkim fanem tego i nie wierzę, że tak naprawdę jest to „struktura kontrolna”, ale raczej element konstrukcyjny w językach funkcjonalnych i stylu programowania. Node.js wydaje się raczej uzależniony.
Josh K

1
Oczywiście jest to struktura kontrolna. Po prostu nie jest to słowo kluczowe takie jak „if” lub „for”, ale jako wzorzec struktury przepływu kontroli (stąd struktura kontroli). Jest nawet używany za kulisami przez wiele kompilatorów. I nie ogranicza się to również do FL. Potrzebujesz jednak funkcji jako obiektów pierwszej klasy.
pillmuncher

1
Obecnie dostajesz optymalizację połączeń ogonowych w C i C ++, ale IMO mija się z celem. Chodzi o to, że jest to optymalizacja. Na schemacie prawdziwe wezwanie do ogona jest oczywiste. Zwłaszcza w C ++ wiele rzeczy może oznaczać, że twoje wywołanie ogona nie jest ogonem ogona. Przepełnienie stosu oznacza, że ​​aplikacja jest zepsuta. IMO powinno zawierać coś w rodzaju goto return ...;instrukcji, która wyraźnie określa zamiar wywołania ogona, więc jeśli kompilator nie może wykonać iteracji, jest to błąd.
Steve314,

1
@Macneil - o czym wiem, że optymalizacja wywołania ogona odbywa się w GCC, Clang i Visual C ++. GCC ma bardziej wyrafinowane konwersje od rekurencji do iteracji, które mogą obsłużyć wiele przypadków, które nie są rekurencją ogona. Ale wiele może się nie udać. Przekaż wskaźnik do zmiennej lokalnej w tym wywołaniu ogona, a ramki stosu nie można wyeliminować, ponieważ zmienna musi być utrzymywana przy życiu. W C ++ lokalne zmienne destruktory zwykle występują po powrocie wywołania „tail”, co oznacza, że ​​wcale nie jest to wywołanie tail.
Steve314,

1
@Mike - o to mi chodzi. Jeśli używasz stylu kodowania rekurencyjnego, bez „optymalizacji” wywołania ogona kod jest potencjalnie uszkodzony, ponieważ przepełnienie stosu jest błędem. W C ++ jest to optymalizacja - kompilator wykonuje optymalizacje, więc nie musisz nic robić, ale nie możesz na nich polegać. Jeśli chcesz swobodnie pisać w stylu rekurencyjnym, ale nie chcesz martwić się o problemy z głębokością stosu, eliminacja wywołania ogonem nie jest optymalizacją - jest to kwestia poprawności. Jeśli rekurencja jest najlepszym sposobem na kodowanie czegoś, powinieneś być w stanie to zrobić - nie musisz się martwić o przepełnienie stosu.
Steve314,

3

Bezproblemowe rozgałęzianie wątków, ma składnię podobną do funkcji, ale wykonuje się w osobnym wątku i nie może uzyskać dostępu do danych, które początkowo nie zostały do ​​niego przekazane.

branch foo(data, to, be, processed){
    //code
    return [resulting, data]
}

Po wywołaniu gałęzi natychmiast zwróci uchwyt.

handle=foo(here, is, some, data)

Za pomocą uchwytu można sprawdzić, czy zadanie zostało wykonane.

handle.finished() //True if the execution is complete

Jeśli wynik zostanie zażądany przed zakończeniem wykonywania, główny wątek po prostu zaczeka.

[result, storage]=handle.result()

Nie obejmowałoby to bardziej zaawansowanych scenariuszy wielowątkowych, ale raczej zapewniałoby łatwo dostępny sposób rozpoczęcia korzystania z wielu rdzeni.


Spójrz na Cilk, jest to bardzo czyste i proste rozszerzenie C: en.wikipedia.org/wiki/Cilk . Nie wiem, czy ma handle.finished()test, ale spawni syncwszystko, czego potrzebujesz do 90% zadań programowania równoległego.
j_random_hacker

3
if (cond)
   //do something
else (cond)
   //do something
else (cond)
   //do something
first
   //do something
then
   //do something
else (cond)
   //do something
else
   //do something
end

Bloki FIRST i THEN działają, jeśli którykolwiek z 3 warunków warunkowych zostanie oceniony jako prawdziwy. PIERWSZY blok działa przed blokiem warunkowym, a THEN po uruchomieniu bloku warunkowego.

Zapis warunkowy lub końcowy ELSE po instrukcji FIRST i THEN są niezależne od tych bloków.

Może czytać jako:

if (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   //do something
else
   //do something
end


function first()
   //do something
return
function then()
   //do something
return

Te funkcje to tylko formularz do czytania. Nie stworzyliby zasięgu. To bardziej jak gosub / powrót z Basic.

Przydatność i czytelność jako przedmiot dyskusji.


2

Czasami piszę pętlę, która musi zrobić coś innego podczas pierwszej iteracji. Na przykład wyświetlanie tagów <th> zamiast tagów <td>.

Sytuację tę rozwiązuję za pomocą flagi logicznej. Coś takiego:

first = true

while (some_condition)
    if (first)
        do_something
        first = false
    else
        do_something_else

Głupio wydaje się sprawdzanie wartości firstkażdej iteracji, gdy przez większość czasu będzie ona fałszywa.

Chciałbym mieć opcję zapętlania, aby określić inną treść pętli podczas pierwszej iteracji. Nie byłoby potrzeby oddzielnej zmiennej. Skompilowany kod też nie potrzebuje jednego, ponieważ wygenerowany kod miałby dwa ciała, jeden dla pierwszej iteracji, a drugi dla reszty.


Pytanie SO ma podobny pomysł, ale z operatorem „wtedy” do użycia na wartościach. W ten sposób prawdopodobnie nie masz zduplikowanego kodu. Np.print(out, first "<th>" then "<td>")
Macneil

1
Lepszym sposobem jest rozpoczęcie iteracji +1.
Josh K

1
Częstym przypadkiem jest zapętlanie między obsługą, na przykład wyświetlanie listy elementów z separatorem przecinkowym. Ściśle to if (!first) gimme-a-comma ();, ale w zasadzie to samo. Miałbym jednak zastrzeżenie - jeśli odpowiednio go zawrzesz, skończysz z takimi metodami, jak metoda łączenia ciągów w języku Python - jakkolwiek często potrzebujesz podstawowego wzorca, nie trzeba tak często przepisywać podstawowej pętli.
Steve314,

Jak mówi Josh, oczywiście wyciągnięcie pierwszego elementu z pętli jest prawidłowe. W przypadku separatora przecinków oznacza to zduplikowany kod, ale może to być powielone wywołanie funkcji. Osobiście wolę nieefektywność if (!first), ale mikrooptymalizatorzy mogą budzić zastrzeżenia dotyczące przewidywania gałęzi.
Steve314,

1
@Barry Brown: Rozwinięcie pierwszej iteracji pętli może, ale nie musi być szybsze niż sprawdzenie flagi boolowskiej. Przyzwoity predyktor gałęzi w najgorszym razie źle przewidzie pierwsze 2 iteracje, przewiduję :) Wolałbym użyć if (!first)i pozwolić kompilatorowi optymalizującemu zdecydować, czy korpus pętli jest wystarczająco mały, aby rozwinięcie i pozbycie firstsię było wygraną netto.
j_random_hacker

1

[skopiowane z mojej własnej odpowiedzi na stackoverflow]


ignoring - Aby zignorować wyjątki występujące w określonym bloku kodu.

try {
  foo()
} catch {
  case ex: SomeException => /* ignore */
  case ex: SomeOtherException => /* ignore */
}

Ignorując konstrukcję kontrolną, możesz napisać ją w bardziej zwięzły i czytelny sposób:

ignoring(classOf[SomeException], classOf[SomeOtherException]) {
  foo()
}

[Scala udostępnia tę (i wiele innych konstrukcji kontroli wyjątków) w swojej standardowej bibliotece, w pakiecie util.control. ]


4
Wyjątków nie należy ignorować.
Josh K

1
Tyle że w kontekście wyjątek nie może być błędem.
Steve314,

1
@Josh, @Steve: Są przypadki, w których chcesz zignorować wyjątki. Zobacz ten wątek w niektórych takich przypadkach.
missingfaktor

Wyjątkiem jest ostrzeżenie, że coś może być nie tak. Pusty try..catchblok to jedno; zmusza cię do rozpoznania błędu i celowego zignorowania go; podczas gdy wyrzucanie fragmentów kodu pod globalne ignorowanie może prowadzić do problemów, gdy te wyjątki są zgłaszane, a także prowadzić do złych nawyków programistycznych.
Josh K

@Josh - Właśnie usunąłem dwa komentarze, ponieważ nie myślałem wprost - ale mam duże nadzieje na ten. Jeśli to stwierdzenie ma charakter globalny, prawdopodobnie się zgadzam - ale dla mnie wygląda to na strukturę blokową. IOW jest jak tryblok, tyle że zawiera listę wyjątków, które wychwytuje i ignoruje z przodu, a nie później. Może to nawet stanowić zaletę w zakresie czytelności - np. Powiadomienie czytelnika, że ​​brakujący plik nie jest błędem, nawet przed odczytaniem otwartego wywołania.
Steve314,

1

Zamiast:

switch(myEnum) {
  case MyEnum.Val1: do1(); ...
  case MyEnum.Val2: do2(); ...
....

Zrób to w Pythonie, a teraz także w języku C #:

action = val2func[myEnum]
action()

Scala ma wiele nowych funkcji.

Wreszcie, języki takie jak Clojure można rozszerzyć, aby zapewnić dodatkową funkcjonalność.


1
C też może to zrobić. I Pascal. Prawdopodobnie wszystkie te stare języki z lat 70. / 80. / 90. Tablica funkcji staje się tablicą wskaźników funkcji, ale to nic wielkiego. Łatwiej jest wtedy, gdy masz anonimowe funkcje - wiesz, jak w Lisp, ML, ...
Steve314

Steve314 - korekta - jest to przykład słownika, który odwzorowuje dowolną wartość na funkcję. To tutaj rzeczy stają się nieco czystsze niż większość rzeczy z lat 70. / 80. / 90.
Job

1

Mam dwa pomysły.

Często stwierdzam, że powtarzam się w catchblokach. Można to nieco pomóc poprzez wyodrębnienie metod, ale może to powodować niepotrzebne zaśmiecenie, jeśli metody są bardzo krótkie lub w inny sposób nie są warte metody. Byłoby więc dobrze zagnieżdżać catchbloki:

try {
    // Save something
} catch (Exception e) {
    // Something we do for all Exceptions
    catch (ProcessingException e) {
        // Something we do for all Processing exceptions
        catch (DBExcpetion e) {
            // DBExceptions are a subclass of ProcessingException
        }
        catch (BusinessRuleException e) {
            // BusinessRuleExceptions are also a subclass of ProcessingException
        }
    }
    // Something we do after specific sub class Exceptions
 }

W programowaniu internetowym często też robię coś takiego (to nie jest prawdziwy przykład, więc nie analizuj fikcyjnych przypadków):

Account a = getSavedAccount();
if (a == null) {
    a = getAccountFromSessionId();
}
if (a == null) {
    a = getAccountFromCookieId();
}
if (a == null) {
    a = createNewAccount();
}

W Javascript (cóż, ECMAScript i może inne, których nie znam), ponieważ każda wartość może być oceniona jako warunek, ||może pomóc.

var a = getAFromLocation1() || getAFromLocation2() || default;

Naprawdę podoba mi się, jak to wygląda i chciałbym, żeby więcej języków, szczególnie po stronie serwera, miało wsparcie. (PHP może ocenić wszystko jako warunek, ale konwertuje całe wyrażenie warunkowe na wartość logiczną zamiast zachowywać wartość. Nie wiem o Pythonie lub Ruby.) Może stać się niewygodny po około trzech przypadkach, ale jeśli masz więcej niż trzy przypadki, możesz mieć również zły projekt oprogramowania.


Python robi coś takiego jak twój || oceny, ale kiedy w końcu dostał swoją wyrażenie warunkowe x if c else yskładni, głównym powodem, że się było, ponieważ wiele wyrażeń za pomocą tych semantykę ||i &&były subtelnie buggy. IIRC częstym przypadkiem była wartość (taka jak zero), która była ważna dla aplikacji traktowanej jako false, a zatem została odrzucona, aby zamiast tego użyto nieprawidłowej rezerwy. Jednak - patrz moja odpowiedź WRT Język programowania ikon, który może mieć takie wyrażenia jak get1() else get2() else default.
Steve314,

1

Uogólniony przełącznik powiedział powyżej:

 switch(x){
  predicate1:
     dosomething();
  predicate2:
     dosomethingelse();
 }

W Haskell:

  switch' :: a -> [(a -> Bool, b)] -> b
  switch' a [] = undefined
  switch' a (f,b):xs = if f a
                     then b
                      else switch' a xs

0

W C # chciałbym używać prostych switch () { ... }, ale rozszerzalnych z takimi wyrażeniami:

switch (value)
{
  // string-based operators:
  case begins "Maria": // to catch Maria Carey
    break;
  case ends "Washington": // to catch George Washington
    break;
  case like "ph": // to catch Phil, Phillip, Sophie
    break;
  case between "Aaron" and "April": // to catch all names between
    break;

  // use non-static variables in case expression:
  case Dao.GetDefaultBabyName():
    break;

  // continuable cases without breaking
  case "John":
    bonus = 25;
  case "Peter":
    salary = 500;
    break;

  // jumps between cases
  case "Aleron":
    // do something
    break;
  case "Bella":
    // do something
    jump "Aleron";
    break;

}

I tak dalej. To samo z numerami lub innych rodzajów (który obsługuje IComparable, IConvertible...)

To może uczynić mój kod bardziej lakonicznym i czytelnym.


Awaria w sprawach jest znanym złem i ogólnie jestem w porządku bez tego. I skoki są zbyt GOTOWE dla jakiegokolwiek rozsądnego programowania. Ale posiadanie wyrażeń i niestatyczności w tym przypadku byłoby uroczym dodatkiem.
CodexArcanum

0

To zabawne pytanie, jak powiedział @Macneil.

Moją ulubioną niezwykłą strukturą kontrolną, którą odkryłem (skromny kaszel), jest wykonywanie różnicowe .

Ma pewne zastosowania. Dla mnie przytłaczające zastosowanie to programowanie interfejsów użytkownika, co jest przykładem bardziej ogólnego problemu utrzymywania nadmiarowych danych w korespondencji. Z jednej strony są dane aplikacji, az drugiej strony są kontrolki interfejsu użytkownika, które muszą być uzgodnione. To brzmi jak „wiązanie”, ale tak naprawdę jest o wiele więcej.

Zwykle implementuję go za pomocą makr w C lub C ++. W C # muszę to zrobić ręcznie rozwijając instrukcje. To jest ból, ale działa.

Kiedyś zaimplementowałem to pod kątem makr Lisp, a potem było bardzo czyste. Nie wymagało to od programisty ostrożności. Mógłbym zrobić to samo w innym języku ustrukturyzowanym, gdybym zadał sobie trud napisania kompletnego parsera, a następnie wygenerowania wszystkich właściwych rzeczy. To duży projekt i jeszcze tego nie zrobiłem.


0

„Tradycyjne” struktury kontrolne, takie jak forkontrolowanie pracującego człowieka, utrzymują go podporządkowanym skorumpowanym ideologiom rządzącej elity kapitalistycznej. Dlatego używam alternatywnych struktur kontrolnych takich jak ph0rzamiast. To jak for, ale bardziej radykalne: nie złapiesz ph0rnoszenia garnituru i krawata, wyrzucając korporacyjne BS. ph0rutrzymuje to, stary.

Walcz z mocą!


0

Najprostsza forpętla

for(100)
{
    //Will run for 100 times
}


for(i)
{
    //Will run for i times while i must be a positive integer
}


for(i as a)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 0 and 
    //scoped within the loop
}


for(i as a=2)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 2 and 
    //scoped within the loop
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.