Quali utili strutture di controllo alternative conosci? [chiuso]

12

La domanda simile è stata chiusa su SO.

A volte, quando programmiamo, scopriamo che alcune strutture di controllo particolari sarebbero molto utili per noi, ma non sono direttamente disponibili nel nostro linguaggio di programmazione.

Quali strutture di controllo alternative ritieni siano un modo utile per organizzare il calcolo?

L'obiettivo qui è quello di ottenere nuovi modi di pensare sulla strutturazione del codice, al fine di migliorare il chunking e il ragionamento.

Puoi creare una sintassi wishful / semantica non disponibile o citare una struttura di controllo meno conosciuta su un linguaggio di programmazione esistente.

Le risposte dovrebbero dare idee per un nuovo linguaggio di programmazione o per migliorare un linguaggio reale.

Pensa a questo come a un brainstorming, quindi pubblica qualcosa che pensi sia un'idea folle ma può essere praticabile in alcuni scenari.

Riguarda la programmazione imperativa.

    
posta Maniero 28.11.2010 - 17:11
fonte

22 risposte

14

OK, questa è una domanda divertente.

Vorrei anche avere un else generale per while e per i loop, per quando la condizione non è vera nel test first :

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

Questo evita l'imbarazzante ricalcolo della condizione o l'immagazzinamento in una variabile.

    
risposta data 28.11.2010 - 18:07
fonte
10

Perché non schiacciare alcune risposte in una?

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.

Una sintassi costruttiva di controllo del flusso estensibile e generalizzata in un linguaggio imperativo sarebbe piuttosto utile e divertente. Fino a quando non compare, suppongo che userò semplicemente Lisp o qualcosa del genere.

    
risposta data 28.11.2010 - 23:00
fonte
7

Strutture di controllo come funzioni.

Voglio for , if , else , while , ecc per essere funzioni, non strutture speciali.

Voglio return , try/except e goto per essere derivati di continuazioni.

Questo naturalmente ha meno a che fare con una particolare struttura di controllo e più a che vedere con il modo in cui vedi le strutture di controllo in generale, la meta delle strutture di controllo.

    
risposta data 29.11.2010 - 10:12
fonte
6

L'articolo collegato ha sicuramente ragione su N + 1/2 Loops di Donald Knuth. Espresso in C / C ++ / Java:

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

Questo è utile per leggere linee o caratteri da un file, testare se hai raggiunto EOF e poi elaborarlo. Sono così abituato a vedere il pattern for(;;)..if(..)break; che sembra idiomatico per me. (Prima di leggere l'articolo di Knuth, ristampato nel libro Literate Programming , questo era un "wtf?".)

Knuth ha suggerito le parole chiave loop/while/repeat :

loop:
  S;
while C:
  T;
repeat

Dove S e T sono segnaposto per una serie di zero o più istruzioni e C è una condizione booleana. Se non ci fosse un'istruzione S , allora sarebbe un ciclo while, e se non c'era un'istruzione T , allora sarebbe un ciclo do.

Questo costrutto stesso potrebbe essere generalizzato consentendo zero o più clausole while C , rendendolo perfetto per esprimere loop infiniti e quindi alcune condizioni più rare che avrebbero bisogno di due controlli.

Nello stesso articolo, Knuth ha suggerito un meccanismo di segnalazione che sarebbe una versione locale delle eccezioni di lancio / cattura (in alternativa all'utilizzo di goto).

Per me? Vorrei che Java ottimizzasse l'ottimizzazione della coda di chiamata, in modo che potessi esprimere qualsiasi struttura di controllo generale, se necessario.

Aggiornamento: Ho dimenticato di menzionare che molti programmatori C / C ++ / Java aggirano questo usando un'assegnazione incorporata nella condizione while :

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

Usando i termini del costrutto di Knuth, questo è ammissibile quando S e C possono essere combinati in una singola espressione. Alcuni odiano vedere l'assegnazione incorporata sopra, mentre altri odiano vedere il break nella for (;;) sopra. Ma quando S e C non possono essere combinati, come quando S ha più istruzioni, for (;;) è l'unica alternativa senza ripetere il codice. L'altra alternativa è duplicare semplicemente il codice S :

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

L'%% di% di alternativa di Knuth sembra molto meglio.

    
risposta data 28.11.2010 - 17:41
fonte
6

Il linguaggio BCPL aveva un % espressione divalueof che poteva essere usato per girare una sequenza di istruzioni in una singola espressione:

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

Dove some series of statements può essere qualsiasi cosa e l'intero valueof valuta a v .

Questo potrebbe essere utile in Java per quando è necessario calcolare un argomento per chiamare un this() o super() (che richiede che nulla avvenga prima di esso). Certo, potresti semplicemente scrivere un metodo separato, ma quello potrebbe essere un problema se devi passare molti valori locali per il contesto.

Se sei in grado di utilizzare final per le variabili che sono necessarie, puoi già fare un valueof in Java usando le classi interne anonime:

foo(a, b, new Object(){String valueof(){
    String v ...; some series of statements; return v;}}.valueof());
    
risposta data 28.11.2010 - 18:24
fonte
6
unless(condition) {
  // ...
}

fa la stessa cosa di:

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

fa la stessa cosa di:

do {
  // ...
} while(!condition)
    
risposta data 28.11.2010 - 18:21
fonte
5

Su una nota diversa, mi piacerebbe vedere un supporto migliore per gli iteratori nei linguaggi di programmazione. In particolare, per quando vuoi scendere due collezioni a coppie :

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

Alcune lingue dinamiche potrebbero già averlo o supportarle facilmente tramite librerie e macro, ma penso che questo sia nello spirito della tua domanda.

Se i due set non sono della stessa dimensione, allora potrebbe generare un'eccezione o potresti avere un else dopo il ciclo per segnalare che c'era una differenza nelle dimensioni.

Naturalmente, puoi generalizzare questo per andare giù di tre o più liste.

Aggiornamento: Anche utile sarebbe fare il prodotto cartesiano tra iterabili:

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

che non sarebbe altro che nidi loop:

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

Sono un po 'preoccupato che tra le due notazioni che ho fornito qui ci sia una differenza tra O (n) e O (n ^ 2) nel numero di coppie, con solo il cambiamento di un singolo carattere.

    
risposta data 28.11.2010 - 18:05
fonte
5

C'è il cosiddetto "Dijkstra's Loop" (chiamato anche "Dijkstra's Guarded Loop"). È stato definito in The Guarded Command Language (GCL) . Puoi trovare alcune informazioni su di esso sintassi e semantica nell'articolo di Wikipedia sopra riportato nella sezione 6 Ripetizione: fare .

Al giorno d'oggi conosco un linguaggio di programmazione che supporta direttamente questa struttura di controllo. È Oberon-07 (PDF, 70 KB). E supporta "Dijkstra's Loop" in forma di dichiarazione while. Dai un'occhiata alla sezione 9.6. Mentre dichiarazioni nel PDF sopra.

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

P.S. Questa è una copia di my SO answer .

    
risposta data 29.11.2010 - 19:15
fonte
4

Espressioni in stile icone con backtracking incorporato.

Python ottiene molti benefici dal generatore di icone - e fa un lavoro migliore in generale, IMO. E in linea di principio il backtracking era solo una specie di lancio eccezionale, ma era la semplicità delle espressioni l'equivalente approssimativo di ...

x = (a / b) else c;

per gestire i casi di errore come la divisione per zero.

Dove l'icona è impazzita - nessun operatore di confronto che restituisce booleani. I confronti sono sempre riusciti o hanno fatto marcia indietro, e c'era qualche altro problema semantico che sto cercando disperatamente di ricordare che ... beh, diciamo che probabilmente è più represso che dimenticato.

Ho sempre pensato che dovessero avere un'espressione di if senza altra parte -% tipo di cosa diif (condition, success-value), backtracking se la condizione restituisce false - e lasciare i paragoni strani.

EDIT ricordo - ovvio davvero. Un confronto con due argomenti ha esito positivo o negativo - non calcola un nuovo valore da restituire. Quindi quando succede, cosa restituisce ? Risposta: uno degli argomenti. Ma se scrivi a > b , che è l'argomento logico da restituire - a o b ? E se invece scrivi b < a ? Io penso ha sempre restituito l'argomento giusto, il che ha più senso di qualsiasi cosa, ma in genere mi sembrava ancora l'argomento sbagliato per me.

    
risposta data 29.11.2010 - 00:58
fonte
4

Questa è solo un'idea generale e una sintassi:

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

ANSO è sempre valutata. ELSE funziona come al solito.

Funziona anche per caso. Probabilmente è un buon modo per eliminare la frase break:

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

può essere letto come:

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

Non so se questo è utile o semplice da leggere, ma è un esempio.

    
risposta data 02.12.2010 - 12:56
fonte
3
Mi viene in mente

Continuing Passing Style . Quindi, naturalmente, ti piacerebbe avere Ottimizzazione delle chiamate tail .

    
risposta data 28.11.2010 - 19:44
fonte
3

Branching di thread senza interruzioni, ha una sintassi simile a una funzione, ma viene eseguito in un thread separato e non può accedere ai dati che non sono stati inizialmente passati ad esso.

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

Quando viene chiamato un ramo, restituirà immediatamente un handle.

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

L'handle può essere utilizzato per verificare se l'attività è stata eseguita.

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

Se il risultato viene richiesto prima che l'esecuzione sia completata, il thread principale semplicemente aspetterà.

[result, storage]=handle.result()

Questo non coprirà gli scenari di multithreading più avanzati, ma piuttosto fornirà un modo facilmente accessibile per iniziare a utilizzare più core.

    
risposta data 29.11.2010 - 22:22
fonte
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

I blocchi FIRST e THEN vengono eseguiti se uno dei 3 condizionali viene valutato come true. Il primo blocco viene eseguito prima del blocco condizionale e THEN viene eseguito dopo l'esecuzione del blocco condizionale.

La scrittura ELSE condizionale o finale dopo l'istruzione FIRST e THEN sono indipendenti da questi blocchi.

Può leggere come:

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

Queste funzioni sono solo un modulo da leggere. Non avrebbero creato scope. È più simile a un gosub / return di Basic.

Utilità e leggibilità come argomento di discussione.

    
risposta data 01.12.2010 - 13:52
fonte
2

A volte mi trovo a scrivere un ciclo che deve fare qualcosa di diverso durante la prima iterazione. Ad esempio, visualizzando < th > tag al posto di < td > tag.

Gestisco questa situazione con una bandiera booleana. Qualcosa del genere:

first = true

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

Sembra stupido controllare il valore di first su ogni iterazione quando diventerà falso il più delle volte.

Mi piacerebbe avere un'opzione di loop per specificare un diverso corpo del loop sulla prima iterazione. Non ci sarebbe bisogno di una variabile separata. Il codice compilato non ne avrebbe bisogno, perché il codice generato avrebbe due corpi, uno per la prima iterazione e uno per il resto.

    
risposta data 28.11.2010 - 18:59
fonte
1

[copiato dalla mia risposta su stackoverflow]

ignoring - Per ignorare le eccezioni che si verificano in un determinato blocco di codice.

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

Con un costrutto di controllo che ignora, puoi scrivere in modo più conciso e più leggibile come:

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

[Scala fornisce questo (e molti altri costrutti di controllo per la gestione delle eccezioni) nella sua libreria standard, nel pacchetto util.control. ]

    
risposta data 28.11.2010 - 18:24
fonte
1

Invece di:

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

Fatelo Python, o anche il modo C #:

action = val2func[myEnum]
action()

Scala ha molte nuove funzionalità.

Infine, lingue come Clojure possono essere estese per fornire funzionalità extra.

    
risposta data 29.11.2010 - 05:27
fonte
1

Ho due idee.

Spesso scopro che sono ripetermi in blocchi catch . Questo può essere un po 'aiutato attraverso l'estrazione di metodi, ma ciò può causare inutili confusione se i metodi sono molto brevi o comunque non degni di un metodo. Quindi, sarebbe bello nidificare blocchi catch :

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
 }

Nella programmazione web spesso mi trovo spesso a fare qualcosa del genere (questo non è un esempio reale, quindi non analizzare casi immaginari):

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

In Javascript (beh, ECMAScript, e forse altri che non ho familiarità con), poiché qualsiasi valore può essere valutato come una condizione, || può essere d'aiuto.

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

Mi piace davvero come sembra, e vorrei che più lingue, in particolare alcune sul lato server, avessero il supporto per farlo. (PHP può valutare qualsiasi cosa come una condizione, ma converte l'intera espressione condizionale in un valore booleano invece di preservarne il valore.Non so Python o Ruby.) Potrebbe diventare ingombrante dopo tre casi o giù di lì, ma se ne hai di più di tre casi, potresti anche avere un cattivo design del software.

    
risposta data 29.11.2010 - 19:13
fonte
1

L'interruttore generalizzato ha detto sopra:

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

In Haskell:

  switch' :: a -> [(a -> Bool, b)] -> b
  switch' a [] = undefined
  switch' a (f,b):xs = if f a
                     then b
                      else switch' a xs
    
risposta data 29.11.2010 - 23:15
fonte
0

In C # mi piacerebbe usare semplice switch () { ... } , ma estendibile con tali espressioni:

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;

}

E così via. Lo stesso con numeri o altri tipi (che supporta IComparable , IConvertible , ...)

Questo potrebbe rendere il mio codice più laconico e leggibile.

    
risposta data 29.11.2010 - 16:08
fonte
0

È una domanda divertente, come ha detto @Macneil.

La mia struttura di controllo insolita preferita, che I (umile tosse) ha scoperto, è esecuzione differenziale .

Ha determinati usi. Per me, l'uso travolgente è nella programmazione delle interfacce utente, che è un'istanza del problema più generale di mantenere i dati ridondanti nella corrispondenza. Da un lato, ci sono i dati delle applicazioni e, dall'altro, ci sono i controlli dell'interfaccia utente, che devono essere mantenuti d'accordo. Sembra "vincolante" ma in realtà c'è molto di più.

Di solito lo implemento con macro in C o C ++. In C # devo farlo con affermazioni a espansione manuale. Questo è un dolore, ma funziona.

Una volta l'ho implementato in termini di macro Lisp, e poi è stato molto pulito. Non ha richiesto attenzione da parte del programmatore. Avrei potuto fare la stessa cosa in qualsiasi altro linguaggio strutturato se mi fossi preso la briga di scrivere un parser completo e poi di generare tutte le cose giuste. Questo è un grande progetto, e non l'ho fatto.

    
risposta data 29.11.2010 - 21:00
fonte
0

Le strutture di controllo "tradizionali" come for riguardano il controllo del lavoratore, mantenendolo sottomesso alle ideologie corrotte dell'élite capitalista dominante. Ecco perché uso invece strutture di controllo alternative come ph0r . È come for , ma più radicale: non prenderai ph0r indossando giacca e cravatta, facendo schizzare alcune BS aziendali. ph0r lo mantiene reale, amico.

Combatti il potere!

    
risposta data 30.11.2010 - 09:18
fonte
0

Il più semplice for loop -

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
}
    
risposta data 31.12.2010 - 09:58
fonte