2012-06-29 4 views
11

Sono principalmente un programmatore C++ (quindi un OO/imperativo) e trovo piuttosto bizzarro che si possa avere una sola istruzione per valutazione in un'istruzione condizionale come un'istruzione if in Scheme, un linguaggio funzionale.Esiste la possibilità di più istruzioni all'interno del corpo di un'istruzione condizionale?

Ad esempio:

(let ((arg1 0) (arg2 1)) 
    (if (> arg1 arg2) 
     arg1 
     arg2))) 

esempio Erroneo:

(let ((arg1 0) (arg2 1)) 
    (if (> arg1 arg2) 
     (arg1 (display "cool")) 
     (arg2 (display "not cool")))) 

mi dà un errore di tipo "application Procedura: procedura prevista, data: 2; argomenti sono stati: #void"

Questo può essere risolto posizionando detta istruzione condizionale in istruzioni diverse all'interno di un corpo di una funzione definita, ad esempio, con il corpo dell'istruzione condizionale che ha sep dichiarazioni arate ogni volta come segue:

(if (condition) statement1a statement2a) 
(if (condition) statement1b statement2b) 

e così via ...

Va da sé che non è troppo pratico. Per non parlare del sovraccarico del codice duplicato.

Mi manca qualcosa qui o c'è davvero nessun altro modo?

risposta

13
(let((arg1 0)(arg2 1)) 
    (if (> arg1 arg2) 
     (begin 
     (display arg1) 
     (newline) 
     (display "cool")) 
     (begin 
     (display arg2) 
     (newline) 
     (display "not cool")))) 

quando si dice (arg1 (disply "cool")) che si sta insinuando che arg1 dovrebbe essere un proceure.

+0

Vero, dovrei averlo notato poiché la sintassi per chiamare la funzione è (nome_file argomenti ...) e una funzione stessa può essere una variabile in Schema. Per non parlare del messaggio di errore ha detto la stessa identica cosa. Grazie per una rapida risposta. –

2

@RajeshBhat ha dato un buon esempio di utilizzo iniziare con un'istruzione if.

un'altra soluzione è la cond modulo

(let ([arg1 0] [arg2 1]) 
    (cond 
    [(< arg1 0) (display "negative!")] 
    [(> arg1 arg2) (display arg1) (newline) (display "cool")] 
    [else (display arg2) (newline) (display "not cool")])) 

Ogni riga nella forma cond ha un implicito begin che si può effettivamente vedere se si guarda l'attuazione del cond.

(link è alla documentazione Chez Scheme, potrebbe (leggi: probabilmente) non essere stessa implementazione si utilizza come è proprietaria, anche se Petite Chez è gratuito (senza compilatore nella versione petite))

http://scheme.com/tspl4/syntax.html#./syntax:s39

Modifica: Nota importante sui moduli di inizio e quindi su tutte le espressioni che hanno implicito l'inizio.

il seguente codice

(+ 2 (begin 3 4 5)) 

viene valutata a 7. Questo perché il valore di ritorno di una forma begin è l'ultima espressione. Questo è solo qualcosa da tenere a mente quando si inizia. Tuttavia, l'uso di effetti collaterali e cose come i display funzionerà perfettamente nelle posizioni in cui sono 3 e 4.

+0

Grazie: lo terrò a mente. Entrambi, se + begin e cond sembrano fare lo stesso lavoro, li userò entrambi regolarmente mentre sto insegnando a me stesso Scheme. –

6

Una cosa che potrebbe mancare è che in Scheme non esiste una "dichiarazione". Tutto è un'espressione e le cose che potresti considerare dichiarazioni restituiscono anche un valore. Ciò si applica a if, che viene in genere utilizzato per restituire un valore (ad esempio, (if (tea-drinker?) 'tea 'coffee).Diversamente dal C++, la maggior parte degli usi dei condizionali non è destinata a modificare una variabile oa stampare valori. Ciò riduce la necessità di avere più espressioni in una clausola if.

Tuttavia, come sottolineato da Ross e Rajesh, è possibile utilizzare cond (consigliato) o utilizzare begin s nelle clausole if. Nota che se hai molti calcoli che effettuano effetti collaterali in un condizionale, potresti non usare lo Schema in modo idiomatico.

+0

Questo mi ha fatto pensare: probabilmente dovrei cercare un modo corretto per implementare le funzioni nello schema. È un approccio alla programmazione completamente diverso rispetto ai linguaggi basati su C.In effetti, anche l'uso della ricorsione è scoraggiato in C++ (una possibilità di overflow dello stack, ironia della sorte), mentre nei linguaggi di Scheme e Lisp è un modo di vivere. Oh e grazie a proposito. –

+1

Hai guardato [Come progettare programmi] (http://www.htdp.org/)? È un manuale che guida l'utente attraverso un processo per la progettazione di funzioni in uno stile funzionale (sebbene alla fine parli anche dell'uso della mutazione). C'è anche un work-in-progress [2a edizione] (http://www.ccs.neu.edu/home/matthias/HtDP2e/index.html) con miglioramenti sostanziali. –

+0

Inoltre, per chiarire cosa sta facendo 'begin', se ho capito bene, crea solo un lamdba e lo applica immediatamente. Cioè, '(inizia uno due tre)' è uguale a '((lambda() uno due tre))'. – d11wtq

1

Dal momento che si sta già utilizzando un processo iterativo nella procedura "interiore", perché non usare questa definizione usando nome consentono

(define (fact n) 
    (let inner ((counter 1) (result 1)) 
    (if (> counter n) 
     result 
     (inner (+ counter 1) (* result counter))))) 

Poiché lo stato del processo può essere determinato con solo 2 variabili, non userà quella quantità di memoria.

per esempio (infatti 6) viene calcolata come questo

(inner 1 1) 
(inner 2 1) 
(inner 3 2) 
(inner 4 6) 
(inner 5 24) 
(inner 6 120) 
(inner 7 720) 
720 

Ecco la versione letrec dello stesso procedimento:

(define (fact n) 
    (letrec ((inner 
      (lambda (counter result) 
       (if (> counter n) 
        result 
        (inner (+ counter 1) (* result counter)))))) 
    (inner 1 1))) 
+0

Ho visto solo tre moduli fino a questo punto: let, let * e letrec. Devo ancora vedere come il fluido, il let e il nome, lascia funzionare. Il tuo codice è ovviamente più compatto del mio, ma sei sicuro che anche i costi della memoria sono inferiori? La mia funzione utilizza 3 variabili oltre alla mutazione. –

+0

modificato per aggiungere la versione di letrec per la vostra convenienza. Funziona esattamente allo stesso modo. Un processo ricorsivo richiede molta memoria poiché deve ricordare gli stati precedenti. Le funzioni che ho definito sopra possono essere ricorsive, ma il processo ** è iterativo **. non richiede memoria degli stati precedenti. –

+0

Vedo, non pensavo fosse iterativo. Ma ora, a un'ispezione più ravvicinata, posso vedere che la sua iterazione sta avvenendo nel secondo argomento della funzione interiore, ignorando la necessità dell'insieme! (incarico) dichiarazioni. Grazie per aver fornito un'alternativa elegante. –

1

Se ti senti limitato dalla sintassi di schema, si può sempre cambiare la sintassi definendo una macro. Una macro è come una lambda, tranne che genera codice in fase di compilazione (come un modello C++) ei suoi argomenti non vengono valutati prima che la macro venga richiamata.

È possibile creare facilmente una macro per consentire di utilizzare la sintassi che normalmente indica l'applicazione della procedura, ad esempio (arg1 "cool"), per indicare "visualizza tutto all'interno delle parentesi con una nuova riga dopo ogni elemento". (Vorrà dire che solo all'interno della macro, ovviamente.) Ti piace questa:

(define-syntax print-if 
    (syntax-rules() 
    [(_ c (true-print ...) (false-print ...)) 
     (if c 
      (display-with-newlines true-print ...) 
      (display-with-newlines false-print ...))])) 

(define-syntax display-with-newlines 
    (syntax-rules() 
    [(_ e) 
     (begin (display e) (newline))] 
    [(_ e0 e* ...) 
     (begin (display-with-newlines e0) (display-with-newlines e* ...)) ])) 

(let ([arg1 0] [arg2 1]) 
    (print-if (> arg1 arg2) 
      (arg1 "cool") 
      (arg2 "not cool"))) 

uscita:

1 
not cool 

Non ti preoccupare se non si capisce come le definizioni di macro funzionano a destra adesso. Se stai provando Scheme dopo aver imparato C++, senza dubbio stai vivendo un sacco di frustrazione. Dovresti dare un'occhiata al tipo di potere e flessibilità che lo Schema ha davvero.

Una grande differenza tra le macro Scheme e i modelli C++ è che in una macro è disponibile l'intero linguaggio Scheme. Una macro dice, usando Scheme, come trasformare un s-expr in codice Scheme, in qualsiasi modo completamente arbitrario che ti piace. Il compilatore quindi compila il codice Scheme prodotto dalla macro. Poiché i programmi Scheme sono essi stessi s-expr, non ci sono essenzialmente restrizioni (oltre allo scope lessicale e la necessità di racchiudere tutto tra parentesi).

E non permettere a nessuno di scoraggiarti dall'usare gli effetti collaterali se lo desideri. La gloria di Scheme è che puoi fare quello che vuoi.

+0

Cool, grazie! Dovrei essere in grado di fare tutti i tipi di cose pazze con esso, a condizione di padroneggiare il sistema macro di Scheme. –

Problemi correlati