#+TITLE: Structure and Interpretation of Computer Programs #+AUTHOR: Lio Novelli * Foreword and Preface #+begin_quote Lisp je preživeli, v uporabi je že "polovico stoletja". #+end_quote #+begin_quote The discretionary exportable functionality entrusted to the individual Lisp programmer is more than an order of magniture greater than that to be found within Pascal enterprises. #+end_quote #+begin_quote Želimo vzpostaviti idejo, da programski jezik ni samo način, da računalnik izvaja operacije, ampak da je predvsem nov formalni medij za izražanje idej o metodologiji. Zato morajo biti programi napisani predvsem zato, da jih ljudje berejo, in slučajno, da jih izvajajo računalniki. Bistvena tema ni sintaksa določenih struktur v programskem jeziku, niti ..., temveč tehnike nadzora intelektualne kompleksnosti veliki programskih sistemov. #+end_quote #+begin_quote Naš pristop k temi izvira iz prepričanja, da "computer science" ni znanost in da ima njen pomen bolj malo opraviti z računalniki. Računalniška revolucija je revolucija v načinu mišljenja in izražanju idej. Bistvo teh sprememb najbolše opiše pojem _proceduralne epistemologije_, ki se ukvarja s strukturo vednosti z imperativnega stališča za razliko od klasične matematike, ki je bolj deklerativna. Matematika postavi okvir za natančno spoprijemanje s pojmovanjem "kaj je". Računanje pa ponudi okvir za natančno ukvarjanje s pojmovanjem "kako". #+end_quote * 1. Grajenje abstrakcij s procedurami ** Elementi programiranja - Primitivni izrazi :: predstavtljajo najpreprostejše gradnike (entitete) programskega jezika - Načini kombinacije, :: s katerimi so sestavljeni elementi zgrajeni iz preprostejših - Načini abstrakcije, :: s katerimi so lahko sestavljeni elementi poimenovani in omogočajo upravljanje z njimii kot enotami ** Izvajanje kombinacij(e) Postopek za izvajanje kombinacij: 1. Izvedi podizraz kombinacije. 2. Uporabi/uveljavi proceduro, ki je najbolje levi podizraz (operator) z argumenti, ki so vrednosti drugih podizrazov (operandi). Postopek evalvacije je rekurziven, saj drugi korak v sebi vključuje prvega, oziroma vključuje svojo definicijo. Tako se zgradi akumulacijsko drevo. Na koncu vedno prideš do točke, ko izvajaš primitivne izraze, ki so: - vrednosti numeričnih števk, ki jo označujejo. - vrednosti vgrajenih operatorjev so strojni ukazi sekvenc, ki izvedejo te operacije. - vrednosti drugih imen so objekti asociirani s temi imeni v okolju. Drugo pravilo je poseben primer tretjega pravila. Simboli + in * so tudi vključeni v globalno okolje in so asociirani s strojnimi ukazi, ki so njihove vrednosti. *Pomembno je prepoznati vlogo okolja pri določanju pomena simbolov v izrazih.* To pravilo se ne nanaša na _posebne oblike (special forms)_. ~define~ je posebna oblika. ** 1.1.4 Sestavljene procedure - Številke in aritmetične operacije so primitivni podatki in procedure. - Gnezdenje kombinacij omogoča način za združevanje operacij. - Definicije, ki asociirajo imena z vrednostmi omogočajo omejene načine abstrakcije. ~(define (square x) (* x x))~ ~(define square (lambda (x) (* x x)))~ ** 1.1.5 Substitucijski model za izvajanje procedur Za izvajanje sestavljenih procedur z argumenti, izvedeš telo procedure z vsakim formalnim parametrom, ki ga nadomestiš s pripadajočim argumentom. _ergh, tukaj se zapletam s slovenskimi prevodi_ _kaj je application in kaj evaluation?_ Načini, na katere deluje interpreter (prevajalnik): - Aplikativni vrstni red :: Najprej evalviraj operator in operande, potem pa izvedi proizvedeno proceduro s pridobljenimi argumenti. - Normalni vrstni red :: Ne izvajaj operandov dokler njihove vrednost niso potrebne. Najprej zamenjaj izraze operandov s parametri, dokler ne pride do izraza, ki vsebuje zgolj primitivne izraze in potem izvedi (vso) evalvacijo. ** meta Linki: https://develop.spacemacs.org/layers/+lang/scheme/README.html https://www.nongnu.org/geiser/ https://www.gnu.org/software/guile/learn/ https://spritely.institute/static/papers/scheme-primer.html#introduction Kako nastavit spacemacs, in malo o guile-u. *** video lekcije https://yewtu.be/channel/UCEBb1b_L6zDS3xTUrIALZOw (6.001 SICP: Structure and Interpretation of Computer Programs (2004)) https://yewtu.be/playlist?list=PL7BcsI5ueSNFPCEisbaoQ0kXIDX9rR5FF (MIT 6.001 Structure and Interpretation, 1986) ** vaje *** 1.3 **** najprej narobe Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers. #+begin_src scheme (define (sum-of-large x y z) (+ (if (> x y) (* x x) (* y y)) (if (> y z) (* y y) (* z z)) ) ) (sum-of-large 3 8 5) #+end_src #+RESULTS: : 128 #+begin_src scheme (define (sum-of-larger x y z) (let* ((s (lambda (a) (* a a))) (sl (lambda (b c) (if (> b c) (s b) (s c)))) ) (+ (sl x y) (sl y z)) )) (sum-of-larger 3 8 5) #+end_src #+RESULTS: : 128 **** pravilno #+begin_src scheme (define (sum-squares-of-larger x y z) (if (> x y) (if (> y z) (+ (* x x) (* y y)) (+ (* x x) (* z z)) ) (if (> x z) (+ (* y y) (* x x)) (+ (* y y) (* z z)) ) ) ) (sum-squares-of-larger 9 10 8) #+end_src #+RESULTS: : 181 *** 1.5 Aplikativni vrstni red: pade takoj v neskoncno zanko. Normalni vrstni red: izvrsi test in pride v if, ki ne izvrsi drugega dela. *** 1.6 [[file:sqrt-newton.scm][sqrt-newton.scm]] *** 1.7 - ~good-enough?~ ni vredu za iskanje korenov majhnih stevil. - pravtako za zelo velika stevila - napisi alternativno ~good-enough?~ proceduro, ki bo gledala, kdaj so spremembe dovolj majhne in takrat prekini funkcijo. // Poglej v sqrt-newton.scm *** 1.8 // Glej v sqrt-newton.sqm ** 1.1.8 Procedure kot crne skatle abstrakcij - block structure - lexical scoping ** 1.2.2 Drevesna rekurzija ** 1.2.3 Redi rasti ** 1.2.4 Eksponentna funkcija #name: exponent #+begin_src scheme ;; O(n) korakov in O(n) prostora (define (expt b n) (if (= n 0) 1 (* b (expt b (- n 1))) ) ) (define (expt-i b n) (expt-iter b n 1) ) ;; O(n) korakov O(1) prostor (define (expt-iter b cnt prod) (if (= cnt 0) prod (expt-iter b (- cnt 1) (* b prod)) ) ) (define (fast-expt b n) (cond ((= n 0) 1) ((even? n) (square (fast-expt b (/ n 2)))) (else (* b (fast-expt b (- n 1)))) ) ) (define (even? n) (= (remainder n 2) 0)) (define (square x) (* x x)) ;; 1.16 ;; successive squaring (fast-expt) but with iteration. ;; transformation (* a (expt b n)) constant (define (fast-expt-i b n) (fast-expt-iter b n 1) ) (define (fast-expt-iter b n a) (cond ((= n 0) a) ((even? n) (fast-expt-iter (square b) (/ n 2) a)) (else (fast-expt-iter b (- n 1) (* a b))) ) ) ;; I'm not sure why this works. I was just guessing. ;; excersize 1.17 (define (slow-multi a b) (if (= b 0) 0 (+ a (* a (- b 1))) ) ) (define (halve x) (/ x 2)) (define (double x) (* x 2)) (define (fast-multi a b) (cond ((= b 0) 0) ((even? b) (double (fast-multi a (halve b)))) (else (+ a (fast-multi a (- b 1)))) ) ) ;; excersize 1.18 (define (fast-multi-i a b) (fast-multi-iter a b 0) ) (define (fast-multi-iter a b s) (cond ((= b 0) s) ((even? b) (fast-multi-iter (double a) (halve b) s)) (else (fast-multi-iter a (- b 1) (+ s a))) ) ) #+end_src ** 1.2.5 Najvecji skupni deljitel ** 1.2.6 Primer: Iskanje prastevil ** 1.3 Sestavljanje abstrakcij s procedurami visjega reda Procedure, ki spreminjajo druge procedure se imenujejo *procedure višjega reda*. ** 1.3.1 Procedure kot argumenti Primer vsote. #+begin_src guile #+end_src //exercise 1.29 #name: simpson #+begin_src scheme (define (sum term a next b) (if (> a b) 0 (+ (term a) (sum term (next a) next b) ) ) ) (define (integral f a b dx) (define (add-dx x) (+ x dx)) (* (sum f (+ a (/ dx 2.0)) add-dx b) dx) ) (define (sum-s term a next b fact) ;; fact is altering between 4 and 2 (define (check-fact fact) (if (= fact 4) 2 4)) (if (> a b) 0 (+ (* fact (term a)) (sum-s term (next a) next b (check-fact fact)) ) ) ) (define (simpson f a b dx) (define (add-dx x) (+ x dx)) (* (+ (f a) (f b) (sum-s f (add-dx a) add-dx (- b dx) 4) ) (/ dx 3.0)) ) (define (simpson-gizmo f a b dx) (define (add-dxdx x) (+ x dx dx)) (* (+ (* 4 (sum f (+ a dx) add-dxdx b)) (* 2 (sum f a add-dxdx b)) (- (f a)) (- (f b)) ) (/ dx 3.0)) ) (define (cube x) (* x x x)) (list (integral cube 1 2 0.01) (integral cube 1 2 0.001) (simpson cube 1 2 0.01) (simpson cube 1 2 0.001) (simpson cube 1 2 (/ 1 1000)) (simpson-gizmo cube 1 2 0.01) (simpson-gizmo cube 1 2 (/ 1 10000)) (simpson-gizmo cube 1 2 0.00001) ) #+end_src #+RESULTS: | 3.7499625000000045 | 3.7499996249995324 | 3.644925346666673 | 3.7499999999995324 | 3.749893334961112 | // exercise 1.30 #+begin_src scheme (define (sum-i term a next b) (define (iter a result) (if (> a b) result (iter (next a) (+ result (term a))) ) ) (iter a 0) ) #+end_src // excercise 1.30, 1.31. 1.32 #+begin_src scheme ;; Analogno napisi produkt kot vsoto. ;; Pokazi kako izgleda fakulteta. ;; Aproksimacija pi/4 = 2/3*4/3*4/5*6/5*6/7*8/7... (define (produkt-r term a next b) ;; a, b sta spodnja in zgornja meja (if (> a b) 1 (* (term a) (produkt-r term (next a) next b)) ) ) (define (fakulteta-p n) (produkt-r (lambda (x) x) 1 (lambda (x) (+ x 1)) n) ) (define (pribl-pi n) (produkt-r (lambda (a) (/ (* (- a 1.0) (+ a 1.0)) (* a a))) 3.0 (lambda (x) (+ x 2.0)) n ) ) ;; gizmo se je spomnil resitve - dva produkta (zgornji in spodnji) ;; iterativni produkt-i (define (produkt-i term a next b) (define (iter-p a result) (if (> a b) result (iter-p (next a) (* result (term a))) ) ) (iter-p a 1) ) (define (pribl-pi-term a) (/ (* (- a 1.0) (+ a 1)) (* a a))) (define (pribl-pi-next a) (+ a 2.0)) (define (pribl-pii n) (produkt-i pribl-pi-term 3.0 pribl-pi-next n )) #+end_src ** 1.3.2 Sestavljanje procedur z ~Lambda~ Splosna forma ~let~ izraza #+begin_example scheme (let (( ) ( ) ... ( )) ) #+end_example To je okrajsava za #+begin_example scheme ((lambda ( ... ) ) ... ) #+end_example ** 1.3.3 Procedure kot splosne metode Ce pogledamo proceduro za integral, vidimo mocnejse abstrakcije: procedure, ki izrazajo splosne racunske metode, neodvisne od posameznih vkljucenih funkcij. ** 1.3.4 Procedure kot vrnjene vrednosti V splošnem programski jeziki omejujo, kateri komputacijski elemente lahko (koda) spreminja. Elementi z najmanj omejitvami imajo /prvorazredni/ status. Pravice in privilegiji prvorazrednih elementov so: - lahko so poimenovani s spremenljivkami - lahko so podani kot argumenti procedur - lahko so vrnjeni kot rezultati procedur - lahko so vključeni v podatkovne strukture V Lispu imajo, za razliko od drugih programskih jezikov, procedure prvorazredni status. To predstavlja težave za implementacijo, ampak nudi višjo ekspresivno moč programskega jezika. Najvišja cena pri implementaciji procedur s prvorazrednim statusom je, da je potrebno rezervirati prostor za procedurine proste spremenljivke tudi, ko se procedura ne izvaja. V scheme-u so te spremenljivke shranjene v procedurino okolje (poglavje 4.1).