2010-01-15 7 views
7

È perché Pascal è stato progettato per essere così, o ci sono dei compromessi?Perché Pascal vieta la modifica del contatore all'interno del blocco?

O quali sono i pro ei contro per vietare o non vietare la modifica del contatore all'interno di un blocco? IMHO, c'è poco da usare per modificare il contatore all'interno di un blocco.

EDIT:
Potrebbe fornire un esempio in cui abbiamo bisogno di modificare il contatore all'interno del per-block?

E 'difficile scegliere tra la risposta di wallyk e la risposta di cartoonfox, dal momento che entrambi sono così risposta analisi nice.Cartoonfox il problema da aspetto lingua, mentre l'analisi wallyk il problema dalla storia e il mondo reale aspect.Anyway, grazie per tutte le vostre risposte e vorrei ringraziare in particolare Wallyk.

risposta

13

Nella programmazione teoria del linguaggio (e in teoria della computabilità) while e for loop hanno differenti proprietà teoriche:

  • un ciclo while si può mai terminare (l'espressione potrebbe essere proprio TRUE)
  • il numero finito di volte in cui un ciclo FOR deve esecuzione dovrebbe essere nota prima che inizi l'esecuzione. Si suppone che i loop FOR si interrompano sempre.

il ciclo for presente in C non tecnicamente conta come un ciclo FOR perché non necessariamente sai quante volte il ciclo itererà prima di eseguirlo. (Ad esempio, è possibile modificare il contatore di loop per l'esecuzione per sempre)

La classe di problemi che è possibile risolvere con i loop WHILE è strettamente più potente di quelli che si potevano risolvere con il ciclo FOR rigoroso trovato in Pascal.

Pascal è progettato in questo modo in modo che gli studenti abbiano due diversi costrutti di loop con proprietà computazionali differenti. (Se hai implementato PER la C-way, il ciclo FOR sarebbe solo una sintassi alternativa per ...)

In termini strettamente teorici, non dovresti mai aver bisogno di modificare il contatore all'interno di un ciclo for. Se potessi farla franca, avresti solo una sintassi alternativa per un ciclo WHILE.

È possibile trovare maggiori informazioni su "ciclo while computabilità" e "per il ciclo computabilità" in queste dispense CS: http://www-compsci.swan.ac.uk/~csjvt/JVTTeaching/TPL.html

Un'altra tale proprietà è btw che il loopvariable è indefinito dopo il ciclo for. Ciò semplifica ulteriormente l'ottimizzazione

+0

Questa è la migliore risposta fino ad ora – MaD70

+1

"L'istruzione for viene usata quando il numero di iterazioni è conosciuto in anticipo" - Wirth 1973, The Programming Language Pascal (Rapporto rivisto) http://maben.homeip.net/static/S100/software/pascal/1973%20The%20Programming%20Language%20Pascal.pdf –

1

Da For loop

In alcune lingue (non C o C++) l'indice del ciclo è immutabile nel portata del corpo del ciclo, con qualsiasi tentativo di modificare il suo valore essendo considerata una semantica errore. Tali modifiche di sono a volte una conseguenza di di un errore del programmatore, che può essere molto difficile per identificarsi una volta fatto in . Tuttavia, è probabile che vengano rilevate solo le modifiche esplicite dal compilatore . Situazioni in cui l'indirizzo della variabile di loop è passato a come argomento di una subroutine che lo rende molto difficile da controllare, poiché il comportamento della routine è in genere non conoscibile dal compilatore.

Quindi questo sembra essere per aiutarti a non bruciare la tua mano più tardi.

+0

Il problema più grande che ho con questo è che almeno quando ho fatto cose Pascal/Delphi questo * non era * documentato nell'aiuto. Stranamente, penso che abbia funzionato in Turbo Pascal 6 ma non più su Delphi. – Joey

+0

Sì, loopvar non è immutabile in Delphi (usarlo lo rende più costoso mentre). Loopvar è ancora indefinito dopo afaik –

3

può rendere alcune ottimizzazioni (anello srotola per esempio) facili: non è necessario per l'analisi statica complicato per determinare se il comportamento loop è prevedibile o meno.

1

Disclaimer: Sono passati decenni dall'ultima volta che ho fatto PASCAL, quindi la mia sintassi potrebbe non essere esattamente corretta.

Devi ricordare che PASCAL è il figlio di Nicklaus Wirth, e Wirth si è preoccupato molto dell'affidabilità e della comprensibilità quando ha progettato PASCAL (e tutti i suoi successori).

Si consideri il seguente frammento di codice:

FOR I := 1 TO 42 (* THE UNIVERSAL ANSWER *) DO FOO(I); 

Senza guardare procedura FOO, rispondere a queste domande: Questo ciclo mai fine? Come lo sai? Quante volte la procedura FOO è stata chiamata nel ciclo? Come lo sai?

PASCAL vieta di modificare la variabile di indice nel corpo del ciclo in modo che sia POSSIBILE conoscere le risposte a tali domande e sapere che le risposte non cambieranno quando e se la procedura FOO cambia.

+0

Naturalmente, dare per la semantica Pascal non garantisce che il corpo del ciclo for terminerà ... –

7

Pascal è stato originariamente progettato come linguaggio di insegnamento per incoraggiare la programmazione strutturata a blocchi.Kernighan (il K di K & R) ha scritto un saggio (comprensibilmente di parte) sui limiti di Pascal, Why Pascal is Not My Favorite Programming Language.

Il divieto di modifica quella che Pascal chiama la variabile di controllo di un ciclo for, combinato con la mancanza di una dichiarazione break significa che è possibile sapere quante volte il corpo del ciclo viene eseguito senza studiare il contenuto.

Senza una dichiarazione break e non essere in grado di utilizzare la variabile di controllo dopo la fine del ciclo è più di una limitazione che non essere in grado di modificare la variabile di controllo all'interno del ciclo in quanto impedisce di essere in grado di algoritmi di elaborazione di stringhe e array scritto nel modo "ovvio".

Queste e altre differenze tra Pascal e C riflettono le diverse filosofie con cui sono state progettate inizialmente: Pascal per far rispettare un concetto di design "corretto", C per consentire più o meno qualsiasi cosa, non importa quanto pericolosa.

(Nota: Delphi ha una dichiarazione Break tuttavia, così come Continue, e Exit che è come return in C.)

Chiaramente mai necessità essere in grado di modificare la variabile di controllo in un Ciclo for, perché possiamo sempre riscrivere utilizzando un ciclo while. Un esempio in C in cui tale comportamento è utilizzato può essere trovato in K & R sezione 7.3, dove viene introdotta una versione semplice di printf(). Il codice che gestisce '%' sequenze all'interno di una stringa di formato fmt è:

for (p = fmt; *p; p++) { 
    if (*p != '%') { 
     putchar(*p); 
     continue; 
    } 
    switch (*++p) { 
    case 'd': 
     /* handle integers */ 
     break; 
    case 'f': 
     /* handle floats */ 
     break; 
    case 's': 
     /* handle strings */ 
     break; 
    default: 
     putchar(*p); 
     break; 
    } 
} 

Anche se questo utilizza un puntatore come variabile di ciclo, potrebbe ugualmente essere stato scritto con un indice intero nella stringa:

for (i = 0; i < strlen(fmt); i++) { 
    if (fmt[i] != '%') { 
     putchar(fmt[i]); 
     continue; 
    } 
    switch (fmt[++i]) { 
    case 'd': 
     /* handle integers */ 
     break; 
    case 'f': 
     /* handle floats */ 
     break; 
    case 's': 
     /* handle strings */ 
     break; 
    default: 
     putchar(fmt[i]); 
     break; 
    } 
} 
+0

'Turbo pascal' supporto 'break' da molto tempo fa. – Jichao

+1

Sì, ma Turbo Pascal supportava 'break' come estensione di un linguaggio non standard: non fa parte di Pascal standard (ISO 7185). Ho citato le estensioni nel contesto di Delphi come Turbo Pascal è ora solo di interesse storico (potrebbe essere stato il primo linguaggio compilato che abbia mai usato). –

+1

Sì. Intendo dire "Turbo pascal" solo per dimostrare che "Perché Pascal non è il mio linguaggio di programmazione preferito" è in qualche modo obsoleto. – Jichao

1

Probabilmente è sicuro concludere che Pascal è stato progettato per impedire la modifica di un indice for loop all'interno del ciclo. Vale la pena notare che Pascal non è affatto l'unico linguaggio che impedisce ai programmatori di farlo, Fortran è un altro esempio.

Ci sono due motivi validi per la progettazione di un linguaggio in quel modo:

  1. i programmi, in particolare la per cicli in loro, sono più facili da capire e quindi più facile da scrivere e modificare e verificare.
  2. I loop sono più facili da ottimizzare se il compilatore sa che il conteggio del trip tramite un loop viene stabilito prima di entrare nel loop e successivamente invariante.

Per molti algoritmi questo comportamento è il comportamento richiesto; ad esempio, aggiornando tutti gli elementi di un array. Se la memoria serve, Pascal fornisce anche cicli di do-while e cicli di ripetizione-fino. La maggior parte, immagino, gli algoritmi implementati in linguaggi in stile C con modifiche alla variabile indice del ciclo o interruzioni del ciclo potrebbero essere facilmente implementati con queste forme alternative di loop.

Mi sono grattato la testa e non sono riuscito a trovare un motivo convincente per consentire la modifica di una variabile di indice del ciclo all'interno del ciclo, ma poi ho sempre considerato di farlo come un cattivo progetto e la selezione del loop corretto costruire come elemento di buon design.

saluti

Mark

11

Pascal è stato implementato per la prima volta per il CDC Cyber, un mainframe degli anni '60 e '70, che come molte CPU aveva eccellenti prestazioni di esecuzione delle istruzioni sequenziali, ma anche una significativa penalizzazione delle prestazioni per le filiali. Questa e altre caratteristiche dell'architettura Cyber ​​hanno probabilmente influenzato pesantemente il design Pascal dei loop for.

Il Risposta breve è quella che permette l'assegnazione di una variabile di ciclo richiederebbe codice di protezione supplementare e l'ottimizzazione incasinato per le variabili di loop che potrebbero normalmente essere gestiti bene in registri indice a 18 bit. A quei tempi, le prestazioni del software erano molto apprezzate a causa della spesa dell'hardware e dell'incapacità di accelerarlo in qualsiasi altro modo.

lungo risposta

La Control Data Corporation 6600 famiglia, che comprende la Cyber, è un'architettura RISC usando parole di memoria centrale 60-bit a cui fanno riferimento gli indirizzi a 18 bit. Alcuni modelli avevano un'opzione (costosa, quindi non comune), la Compare-Move Unit (CMU), per indirizzare direttamente i campi di caratteri a 6 bit, ma altrimenti non esisteva alcun supporto per "byte" di alcun tipo. Poiché la CMU non può essere calcolata in generale, la maggior parte del codice Cyber ​​è stata generata per la sua assenza.Dieci caratteri per parola rappresentavano il solito formato di dati fino a quando il supporto per i caratteri minuscoli lasciava il posto a una tentativa rappresentazione di caratteri a 12 bit.

Le istruzioni sono lunghe 15 bit o 30 bit, eccetto che le istruzioni CMU hanno una lunghezza effettiva di 60 bit. Quindi fino a 4 istruzioni confezionate in ogni parola, o due 30 bit, o una coppia di 15 bit e una 30 bit. Le istruzioni a 30 bit non possono includere parole. Poiché le destinazioni dei rami possono fare riferimento solo alle parole, i target di salto sono allineati alle parole.

L'architettura non ha stack. In effetti, la procedura chiamata istruzione RJ è intrinsecamente non rientranti. RJ modifica la prima parola della procedura chiamata scrivendo un salto all'istruzione successiva dopo l'istruzione RJ. Le procedure richiamate tornano al chiamante saltando al loro inizio, che è riservato per il collegamento di ritorno. Le procedure iniziano con la seconda parola. Per implementare la ricorsione, la maggior parte dei compilatori ha utilizzato una funzione di supporto.

Il file di registro ha otto istanze ciascuna di tre tipi di registro, A0..A7 per la manipolazione degli indirizzi, B0..B7 per l'indicizzazione e X0..X7 per l'aritmetica generale. I registri A e B sono 18 bit; I registri X sono 60 bit. L'impostazione da A1 a A5 ha l'effetto collaterale di caricare il registro X1-X5 corrispondente con il contenuto dell'indirizzo caricato. L'impostazione A6 o A7 scrive il contenuto X6 o X7 corrispondente all'indirizzo caricato nel registro A. A0 e X0 non sono collegati. I registri B possono essere utilizzati praticamente in ogni istruzione come valore da aggiungere o sottrarre da qualsiasi altro registro A, B o X. Quindi sono grandi per i piccoli contatori.

Per codice efficiente, un registro B viene utilizzato per variabili di loop poiché è possibile utilizzare su di esse istruzioni di confronto diretto (B2 < 100, ecc.); i confronti con i registri X sono limitati alle relazioni a zero, quindi confrontare un registro X a 100, ad esempio, richiede la sottrazione di 100 e il test del risultato per meno di zero, ecc. Se è stata consentita un'assegnazione alla variabile di ciclo, un valore a 60 bit dovrebbe essere controllato da un intervallo prima di essere assegnato al registro B. Questa è una vera seccatura. Herr Wirth probabilmente ha pensato che sia la seccatura che l'inefficienza non valevano l'utilità - il programmatore può sempre usare un ciclo while o repeat ... until in quella situazione.

stranezza Ulteriori

Diversi unico-a-Pascal caratteristiche del linguaggio riguardano direttamente gli aspetti del Cyber:

  • la parola pack: o un unico "personaggio" consuma una parola di 60 bit oppure è composto da dieci caratteri per parola.
  • la (inusuale) Tipo alfa: packed array [1..10] of char
  • procedure intrinseche pack() e unpack() per affrontare con i caratteri confezionati. Questi non eseguono alcuna trasformazione sulle architetture moderne, solo la conversione del tipo.
  • la stranezza dei file text rispetto a file of char
  • nessun carattere di fine riga esplicito. La gestione dei record è stata esplicitamente invocata con writeln
  • Mentre set of char era molto utile su CDC, non era supportato su molte macchine successive a 8 bit a causa dell'eccesso di memoria utilizzata (variabili/costanti a 32 byte per ASCII a 8 bit). Al contrario, una singola parola Cyber ​​poteva gestire il set nativo di 62 caratteri omettendo newline e qualcos'altro.
  • valutazione di espressioni complete (rispetto alle scorciatoie).Questi sono stati implementati non saltando e impostando uno o zero (come fanno la maggior parte dei generatori di codice oggi), ma usando le istruzioni della CPU che implementano l'aritmetica booleana.
+0

+1 per l'aspetto storico – Jichao

+1

È che tu Mel? – ergosys

+0

Grazie, ma Mel mi ha preceduto di alcuni anni. (Era difficile resistere alla descrizione delle unità di elaborazione periferiche dei PPU). – wallyk