Domanda

Sto seguendo Struttura e interpretazione dei programmi per computer e durante il tentativo di risolvere Ex 1.3 sono arrivato al seguente codice come primo tentativo:

(define (sumsq a b c)(
(define highest (if (> (if (> a b) a b) (if (> a c) a c)) (if (> a b) a b) (if (> a c) a c)))
(define second_h (if (> (if (> a b) b a) (if (> a c) c a)) (if (> a b) b a) (if (> a c) c a)))
(+ (* highest highest) (* second_h second_h)))

Non funzionava e ho cercato la soluzione e li ho trovati su SICP Wiki

;; ex 1.3
;; implemented using only techniques covered to this point

(define (square x) (* x x))

(define (sum-of-squares x y)
  (+ (square x) (square y)))

(define (largest-two-of-three x y z)
  (if (>= x y)
      (sum-of-squares x (if (>= y z) y z))
      (sum-of-squares y (if (>= x z) x z))))

La differenza era che stavo usando più istruzioni per definire le variabili e quindi sommare i quadrati, mentre il modo corretto è quello di definire ciascuna delle mie linee come funzioni.

Le funzioni in Scheme sono allineate? O mi sono perso tutto?

È stato utile?

Soluzione

Quello che hai scritto (meno un altro extra) è:

(define (sumsq a b c)
  (define highest
    (if (> (if (> a b) a b)
           (if (> a c) a c))
      (if (> a b) a b)
      (if (> a c) a c)))
  (define second_h
    (if (> (if (> a b) b a)
           (if (> a c) c a))
      (if (> a b) b a)
      (if (> a c) c a)))
  (+ (* highest highest) (* second_h second_h)))

La loro soluzione separa i quadrati e la somma dei quadrati in funzioni separate, ma non penso che sia importante. Non scrivere (+ (* a a) (* b b)) ti ti impedirà di dover nominare i due valori che stai calcolando, il che ti permetterà di scrivere la funzione come una grande espressione alla fine, ma ci sono cose più grandi di cui preoccuparti ora.

Penso che il problema che stai riscontrando sia che le tue espressioni (se ...) sono troppo grandi per essere facilmente comprese. Si noti che esistono due motivi che vengono visualizzati più volte: (if (> a b) a b) e (if (> a b) b a). Queste sono le funzioni max e min, quindi è utile definirle come tali:

(define (min a b) (if (< a b) a b))
(define (max a b) (if (< a b) b a))

In questo modo, puoi riscrivere la tua soluzione come:

(define (sumsq a b c)
  (define highest
    (if (> (max a b) (max a c))
      (max a b)
      (max a c)))
  (define second_h
    (if (> (min a b) (min a c))
      (min a b)
      (min a c)))
  (+ (* highest highest) (* second_h second_h)))

La semplificazione di nuovo dà:

(define (sumsq a b c)
  (define highest
    (max (max a b) (max a c)))
  (define second_h
    (max (min a b) (min a c)))
  (+ (* highest highest) (* second_h second_h)))

Nota come questa scrittura sia molto più facile da ragionare, (max (max a b) (max a c)) è ovviamente il massimo di a b e c e può effettivamente essere riscritto come (max (max a b) c). Guardando second_h, tuttavia, non è ovvio che sia corretto. Cosa succederà quando x è il più piccolo dei tre valori?

Il trucco che usano nella loro soluzione è innanzitutto confrontare y e x < y. se z, allora sai che y < x non è il più piccolo dei tre, quindi è il più alto o il secondo più alto. L'altro numero che si desidera utilizzare è il maggiore tra <=> e <=>, poiché il più basso di questi due sarà il più piccolo dei tre, che si desidera ignorare. Una logica simile si applica quando <=>.

Altri suggerimenti

È necessario utilizzare rientri e interruzioni di riga appropriati per ottenere una panoramica del flusso del programma. La tua prima proposta è quindi la seguente:

(define (sumsq a b c)
  ((define highest 
     (if (> (if (> a b) a b)
            (if (> a c) a c))
         (if (> a b) a b)
         (if (> a c) a c)))
   (define second-h
     (if (> (if (> a b) b a)
            (if (> a c) c a))
         (if (> a b) b a)
         (if (> a c) c a)))
   (+ (* highest highest)
      (* second-h second-h)))

Prima cosa da notare: le parentesi non corrispondono; ce n'è uno più aperto che chiuso. Un'attenta ispezione mostra che una parentesi aperta nella seconda riga è errata. Questo è, per inciso, quello che stava in qualche modo penzolando alla fine della tua prima linea. Immagino che quando hai provato a valutare questo, non è successo nulla, poiché il lettore ha aspettato la fine della dichiarazione.

Il rientro corretto è abbastanza importante. Penso che la SICP non lo spieghi esplicitamente, sebbene gli esempi siano generalmente fatti in questo modo. Ho trovato una guida di stile qui .

Seconda osservazione: ti ripeti molto. In tutte queste affermazioni if nidificate, non sono sicuro che tu abbia davvero ottenuto i valori giusti. Guarda la soluzione che hai trovato per vedere come questo può essere notevolmente semplificato.

Hai provato a spezzare la complessità dando nomi di subresults. Rompere la complessità è buono, ma è generalmente meglio nominare non i risultati, ma i concetti. Pensa a quello che fai, quindi dai un nome a queste attività. Quelle sono funzioni e costituiscono il linguaggio in cui finalmente risolvi quasi banalmente il tuo problema.

Una delle idee di Scheme è bottom-up programming in cui si crea una funzione per ogni singola operazione concettuale. Questo è l'approccio raccomandato in molti linguaggi di programmazione funzionale.

Con questo approccio si hanno molte piccole funzioni che implementano un'operazione logica sugli argomenti. In questo modo il tuo codice diventa molto più modulare e pulito.

La tua soluzione aveva questo modulo: (define (func param) (define ...) (define ...))

Ma definire ha bisogno di questo modulo: (define (func param) body)

Il corpo è l'implementazione della funzione ... cosa fa, cosa restituisce. Il tuo corpo era solo più definizioni, senza mai fare nulla. Questo spiega perché la tua soluzione non è stata accettata dall'interprete Scheme.

Per rispondere alla domanda " sono le funzioni di schema a linea singola? " devi esaminare il " inizia " forma, che assomiglia a questo: (inizio (+ 1 1) (+ 2 2)) = > 4

Nell'esempio sopra, il risultato di (+ 1 1) è appena lanciato, quindi puoi vedere che iniziare ha davvero senso solo quando le cose al suo interno hanno effetti collaterali.

Dovresti essere consapevole che alcune parti di Scheme (in particolare let e lambda) hanno inizio implicite attorno al loro corpo. Quindi questo è valido:

  (let ((x 1))
    (+ 1 1)
    (+ 2 2))

anche senza un inizio. Questo rende il codice più semplice da scrivere.

Infine, mentre continui ad apprendere Scheme, cerca sempre di trovare un modo per fare qualcosa senza inizio e senza effetti collaterali. Soprattutto nei primi capitoli della maggior parte dei libri di Scheme, se stai pensando, & Quot; Voglio impostare quella variabile, quindi voglio fare questo, quindi questo ... & Quot; probabilmente sei intrappolato nel tuo vecchio modo di programmare e non lo fai nel modo Scheme. Non c'è nulla di sbagliato negli effetti collaterali, ma un uso intenso di questi significa che non stai davvero programmando Scheme nel modo in cui funziona meglio.

L'esercizio 1.3 ti chiede di definire una procedura che prende tre numeri come argomenti e restituisce la somma dei quadrati dei due numeri più grandi. È facile definire una procedura come quella usando le procedure dello schema integrate square, max e min, ma non abbiamo ancora incontrato queste procedure a quel punto nel libro, quindi le definirei anche.

(define (square x)
   (* x x))

(define (max x y)
   (if (> x y) x y))

(define (min x y)
   (if (< x y) x y))

(define (sum-of-highest-squares x y z)
   (+ (square (max x y))
      (square (max (min x y) z))))

La procedura sum-of-highest-squares funziona aggiungendo il quadrato del massimo di xey (il massimo di quei due viene eliminato dall'essere il più basso dei tre) e il quadrato del massimo dei restanti due (il minimo di xey, che sarà il valore rimasto dal primo passaggio) e z.

Nota: questo è tratto dal mio post sul blog Esercizi SICP 1.1 - 1.5 . Ci sono collegamenti che ti porteranno anche a molte altre soluzioni SICP.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top