1109 lines
32 KiB
Org Mode
1109 lines
32 KiB
Org Mode
#+TITLE: Structure and Interpretation of Computer Programs
|
|
#+AUTHOR: Lio Novelli
|
|
|
|
* Foreword and Preface
|
|
:PROPERTIES:
|
|
:UNNUMBERED: t
|
|
:END:
|
|
|
|
|
|
#+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
|
|
|
|
* 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.
|
|
|
|
|
|
** 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)))~
|
|
|
|
** 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
|
|
|
|
Tukaj se naucimu successive squaring, ki potem se veckrat prav pride.
|
|
|
|
#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 (slow-multi 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
|
|
;; Russian paesant method - zelo star algoritem.
|
|
(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)))
|
|
)
|
|
)
|
|
|
|
;; excercise 1.19 - fast fibnonachi
|
|
(define (fast-fibo n)
|
|
(fast-fibo-iter 1 0 0 1 n)
|
|
)
|
|
(define (fast-fibo-iter a b p q count)
|
|
(cond ((= count 0) b)
|
|
((even? count)
|
|
(fast-fibo-iter
|
|
a
|
|
b
|
|
(+ (* p p) (* q q))
|
|
(+ (* q q) (* 2 p q))
|
|
(/ count 2)
|
|
)
|
|
)
|
|
(else (fast-fibo-iter
|
|
(+ (* b q) (* a q) (* a p))
|
|
(+ (* b p) (* a q))
|
|
p
|
|
q
|
|
(- count 1)
|
|
))
|
|
)
|
|
)
|
|
;; p' = q^2 + 2pq
|
|
;; p' = p^2 + q^2
|
|
(define (slow-fibo n)
|
|
(cond ((= n 0) 0)
|
|
((= n 1) 1)
|
|
(else (+
|
|
(slow-fibo (- n 1))
|
|
(slow-fibo (- n 2))
|
|
))
|
|
)
|
|
)
|
|
;; melje melje in melje . fast-fibo iypljune takoj
|
|
#+end_src
|
|
|
|
#+RESULTS:
|
|
: #<unspecified>
|
|
|
|
** Najvecji skupni deljitel
|
|
|
|
Evklidov algoritem je zelo star algoritem.
|
|
|
|
Ce je ~r~ ostanek pri deljenju ~a~ z ~b~, potem so skupni deljitelji ~a~ in ~b~
|
|
enaki kot skupni deljitelji kot ~b~ in ~r~.
|
|
*Lamejev teorem* :: Ce evklidov algoritem potrebuje ~k~ korakov, da izracuna NSD
|
|
nekega para, potem mora biti manjsa stevilka v paru vecja ali enaka ~k~-ti
|
|
Fibonaccijevi stevilki.
|
|
*footnote* Gabriel Lame - francoski matematik rojen 1845, ki je postavil veliko
|
|
tez, ampak nobene ni dokazal.
|
|
|
|
Lamejev teorem lahko uporabis za ocen velikosti rasti evklidovega algoritma:
|
|
~n >= Fib(k) =. fi^k/sqrt(5)~. Torej stevilo korako rase logaritemsko.
|
|
|
|
#+begin_src scheme
|
|
(define (gcd a b)
|
|
(if (= b 0)
|
|
a
|
|
(gcd b (remainder a b))
|
|
)
|
|
)
|
|
|
|
;; naloga 1.20
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
** 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.0001)
|
|
)
|
|
#+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
|
|
))
|
|
|
|
;; excercise 1.32
|
|
;; recursive accumulate
|
|
(define (accumulate-r combiner null-val term a next b)
|
|
;; combiner is a procedure of two arguments.
|
|
(if (> a b)
|
|
null-val
|
|
(combiner (term a) (accumulate-r combiner null-val term (next a) next b))
|
|
)
|
|
)
|
|
(define (sum-combiner t acc)
|
|
(+ t acc)
|
|
)
|
|
(define (sum-a term a next b)
|
|
(accumulate-r (lambda (t acc) (+ t acc)) 0 term a next b)
|
|
)
|
|
|
|
(define (prod-a term a next b)
|
|
(accumulate-r (lambda (t acc) (* t acc)) 1 term a next b)
|
|
)
|
|
|
|
(define (accumulate-i combiner null-val term a next b)
|
|
;; Iterative accumulator.
|
|
(define (iter-a a result)
|
|
(if (> a b)
|
|
result
|
|
(iter-a (next a) (combiner (term a) result))
|
|
)
|
|
)
|
|
(iter-a a null-val)
|
|
)
|
|
(define (identity x) x)
|
|
(define (add1 x) (+ x 1))
|
|
(define (sum-ai term a next b)
|
|
(accumulate-i sum-combiner 0 term a next b)
|
|
)
|
|
(define (prod-ai term a next b)
|
|
(accumulate-i (lambda (t acc) (* t acc)) 1 term a next b))
|
|
(define (fakulteta-ai n) (prod-ai identity 2 add1 n))
|
|
|
|
;; excercise 1.33 filtered accumulate - combine only those term derived from
|
|
;; values in the range that satisfy a specified condition (predicate).
|
|
;; a) sum of squares of prime numbers - assuming prime? exists already
|
|
|
|
(define (filtered-accumulate-r combiner null-val predicate term a next b)
|
|
;; combiner 2 args - element and accumulation
|
|
;; predicate 1 arg - a condition when to apply combiner
|
|
;; term 1 arg - a function to compute the term
|
|
;; next 1 arg - compute a next step
|
|
(if (> a b)
|
|
null-val
|
|
(if (predicate a)
|
|
(combiner (term a) (filtered-accumulate-r combiner null-val predicate term (next a) next b))
|
|
;; should I call combiner with null-val instead of (term a) or can I
|
|
;; directly call filtered-accumulate-r?
|
|
(filtered-accumulate-r combiner null-val predicate term (next a) next b)
|
|
)
|
|
)
|
|
)
|
|
;; (filtered-accumulate-r sum-combiner 0 even? identity 1 add1 11)
|
|
(define (filtered-accumulate-i combiner null-val predicate term a next b)
|
|
(define (iter-fa a result)
|
|
(if (> a b)
|
|
result
|
|
(iter-fa (next a)
|
|
(if (predicate a)
|
|
(combiner (term a) result)
|
|
(combiner null-val result)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(iter-fa a null-val)
|
|
)
|
|
#+end_src
|
|
|
|
** 1.3.2 Sestavljanje procedur z ~Lambda~
|
|
|
|
Splosna forma ~let~ izraza
|
|
|
|
#+begin_example scheme
|
|
(let ((<var1> <exp1>)
|
|
(<var2> <exp2>)
|
|
...
|
|
(<varn> <expn>))
|
|
<body>)
|
|
#+end_example
|
|
To je okrajsava za
|
|
#+begin_example scheme
|
|
((lambda (<var1> ... <varn>)
|
|
<body>)
|
|
<exp1>
|
|
...
|
|
<exp2>
|
|
)
|
|
#+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).
|
|
|
|
|
|
* Grajenje absrakcij s podatki
|
|
|
|
Poglavje bo govorilo o kompleksnih podatkih. Poglavje 1 govori o grajenju
|
|
abstrakcij z zdruzevanjem procedur, ki tvorijo sestavljene procedure (compound).
|
|
V poglavju 2 pa bo fokus na grajenju abstrakcij z zdruzevanjem podatkovnih
|
|
objektov v sestavljene podatke (compound).
|
|
|
|
Z zdruzenimi podatkovnimi objekti lahko procedure delajo nad njimi ne da bi bile
|
|
odvisne od njihove natancne strukture.
|
|
|
|
Podobno kot pri sestavljenih procedurah gre tudi pri sestavljenih podatkovnih
|
|
objektih za nacin spoprijemanja s kompleksnostjo - podatkovne abstrakcije
|
|
omogocijo postavitev primernih abstrakcijskih pregrad med razlicnimi deli
|
|
programa.
|
|
|
|
Napoved, kaj se bo pregledalo v 2. poglavju (bi bilo smiselno povzet).
|
|
|
|
** Uvod v podatkovne abstrakcije
|
|
|
|
Podatkovna abstrakcija je metodologija, ki nam omogoci, da locimo kako so
|
|
sestavljeni podatki uporabljeni od detajlov o tem, kako so izgrejeni iz
|
|
primitivnih podatkovnih objektov. (To je analogno grajenju produceur, ki imajo
|
|
vgrajene druge procedue iz poglavja 1.1.8)
|
|
|
|
Skratka programe hocemo graditi tako, da uporabljajo podatke na nacin, da nimajo
|
|
nobenih predpostavk o tem, kaksni naj so ti podatki (oziroma cim manj), ravno
|
|
dovolj za izvajanje potrebnih operacij. Hkrati so konkretne reprezentacije
|
|
podatkov definirane neodvisno od programov, ki podatke uporabljajo.
|
|
|
|
Selektorji in konstruktorji.
|
|
|
|
*** Aritmeticne operacije z racionalnimi stevili
|
|
|
|
#+begin_src scheme
|
|
(define (add-rat x y)
|
|
(make-rat (+ (* (numer x) (denom y))
|
|
(* (numer y) (denom x))
|
|
)
|
|
(* (denom x) (denom y))
|
|
)
|
|
)
|
|
|
|
(define (sub-rat x y)
|
|
(make-rat (- (numer x) (denom y)
|
|
(numer y) (denom x)
|
|
)
|
|
(* (denom x) (denom y))
|
|
)
|
|
)
|
|
|
|
(define (mul-rat x y)
|
|
(make-rat (* (numer x) (numer y))
|
|
(* (denom x) (denom y))
|
|
)
|
|
)
|
|
|
|
(define (div-rat x y)
|
|
(make-rat (* (numer x) (denom y))
|
|
(* (denom x) (numer y)))
|
|
)
|
|
|
|
(define (equal-rat? x y)
|
|
(= (* (numer x) (denom y))
|
|
(* (numer y) (denom x))
|
|
)
|
|
)
|
|
|
|
(define (make-rat n d) (cons n d))
|
|
|
|
(define (numer x) (car x))
|
|
(define (denom x) (cdr x))
|
|
|
|
(define (print-rat x)
|
|
(display (numer x))
|
|
(display "/")
|
|
(display (denom x))
|
|
(newline)
|
|
)
|
|
(define one-half (make-rat 1 2))
|
|
(define one-third (make-rat 1 3))
|
|
|
|
;; excercise 2.1
|
|
|
|
(define (make-rat-norm n d)
|
|
(if (< d 0)
|
|
(make-rat (* n -1) (* d -1))
|
|
(make-rat n d)
|
|
)
|
|
)
|
|
|
|
(define (make-rat-norm-gcd n d)
|
|
(let ((g (gcd n d)))
|
|
(make-rat-norm (/ n g) (/ d g))
|
|
)
|
|
)
|
|
#+end_src
|
|
|
|
Sestavljena strkutura ~par~, ki je konstruirana s primitivno proceduro ~cons~. S
|
|
primitivnimi procedurami ~car~ in ~cdr~ lahko dobimo prvi in ostale elemente
|
|
para.
|
|
|
|
**** Predstavljanje racionalnih stevil
|
|
:PROPERTIES:
|
|
:UNNUMBERED: t
|
|
:END:
|
|
/Glej zgornji codeblock./
|
|
|
|
*** Pregrade abstrakcij
|
|
|
|
Splosna ideja podatkovnih abstrakcij je, da se identificira za vsak tip podatka
|
|
osnovni set opraracij, s katerimi bodo vse procedure, ki bodo manipulirale
|
|
podatke operirale, oziroma bodo iz njih sestavljene. Nato se uporabljamo samo te
|
|
operacije pri delu s podatki.
|
|
|
|
Pregrade:
|
|
- programi, ki uporabljajo racionalna stevila
|
|
- racionalna stevila v problemskem polju
|
|
- ~add-rat~, ~sub-rat~ ...
|
|
- racionalna stevila kot stevci in imenovalci
|
|
- ~make-rat~, ~numer~, ~denom~
|
|
- racionalna stevila kot pari
|
|
- ~cons~, ~car~, ~cdr~
|
|
- kakor so pac pari implementirani
|
|
|
|
Procedure na vsakem nivoju so vmesniki, ki definirajo abstrakcijski nivo in med
|
|
sabo povezujejo razlicne nivoje.
|
|
|
|
Ena od prednosti razdelitve na nivoje je, da je programe lazje vzdrzevati in
|
|
spreminjati, ker lahko delas spremembe na posameznem nivoju, ki ne vplivajo
|
|
izven svojega nivoja.
|
|
/vaja 2.2/
|
|
#+begin_src scheme
|
|
;; crte v prostoru
|
|
(define (make-segment startp endp)
|
|
(cons startp endp)
|
|
)
|
|
(define (make-line x1 y1 x2 y2)
|
|
(make-segment (make-point x1 y1) (make-point x2 y2))
|
|
)
|
|
|
|
(define (start-segment segment)
|
|
(car segment)
|
|
)
|
|
(define (end-segment segment)
|
|
(cdr segment)
|
|
)
|
|
|
|
|
|
(define (make-point x y)
|
|
(cons x y)
|
|
)
|
|
(define (x-point p) (car p))
|
|
(define (y-point p) (cdr p))
|
|
|
|
(define (mid-point segment)
|
|
(make-point
|
|
(/ (+ (x-point (start-segment segment)) (x-point (end-segment segment))) 2)
|
|
(/ (+ (y-point (start-segment segment)) (y-point (end-segment segment))) 2)
|
|
)
|
|
)
|
|
|
|
(define (print-point p)
|
|
(display "(")
|
|
(display (x-point p))
|
|
(display ",")
|
|
(display (y-point p))
|
|
(display ")")
|
|
(newline)
|
|
)
|
|
|
|
;; vaja 2.3 :: segment je lahko tudi pravokotnik, ce nimamo rotacije in je crta
|
|
;; vedno diagonala. Delal bom brez rotacije, zato ker potem ni dovolj imeti
|
|
;; konstruktorja, ki je samo kons, ampak rabim 3 parametre, segment in rotacija
|
|
;; in potem nvm kako delat selektorje in pa se vse se mi zakomplicira in se mi
|
|
;; ne da, ker je nedelja zvecer.
|
|
|
|
;; brez rotacije - 2a + 2b
|
|
(define (perimeter rectangle)
|
|
(+ (* 2 (side-a rectangle)) (* 2 (side-b rectangle)))
|
|
)
|
|
(define (area rectangle)
|
|
(* (side-a rectangle) (side-b rectangle))
|
|
)
|
|
;; selektor (brez rotacije)
|
|
(define (side-a rectangle)
|
|
(abs (- (x-point (start-segment rectangle)) (x-point (end-segment rectangle))))
|
|
)
|
|
(define (side-b rectangle)
|
|
(abs (- (y-point (start-segment rectangle)) (y-point (end-segment rectangle))))
|
|
)
|
|
(define (make-rectangle point-a point-c)
|
|
(make-segment point-a point-c)
|
|
)
|
|
;; sedaj vpeljemo drugo reprezentacijo pravokotnikov (nic vec s segmentom) ali
|
|
;; lahko obseg in ploscina se vedno delujeta? odvisna sta od side-a in side-b.
|
|
;; Ce to zemanjam, bosta obseg in ploscina se vedno delovali.
|
|
(define (mk-rect point-a sirina visina)
|
|
(define (get-point-c point-a sirina visina)
|
|
(cons (+ (x-point point-a) sirina) (+ (y-point point-a) visina))
|
|
)
|
|
(make-rectangle point-a (get-point-c point-a sirina visina))
|
|
)
|
|
#+end_src
|
|
|
|
#+RESULTS:
|
|
: #<unspecified>
|
|
|
|
*** Kaj so podatki?
|
|
|
|
Pri racionalnih stevilih imamo se en pogoj:
|
|
~(/ (numer x) (denom x)) = n/d~
|
|
|
|
Selektorji, konstruktorji in pogoji tvorijo veljavno reprezentacijo.
|
|
|
|
Vsaka trojica procedur, ki ustreza pogoju, da ce zdruzis dva objekta, in potem z
|
|
eno proceduro dobis iz zdruzenih prvi objekt in z drugo drugi objekt, je potem
|
|
trojica procedu za delanje s pari.
|
|
|
|
Trojico procedur (cons, car, cdr) se da implementirati brez podatkov:
|
|
|
|
#+begin_src scheme
|
|
(define (cons-p x y)
|
|
(define (dispatch m)
|
|
(cond
|
|
((= m 0) x)
|
|
((= m 1) y)
|
|
(else (error "Argument not 0 or 1 -- CONS" m))
|
|
)
|
|
)
|
|
dispatch)
|
|
(define (car-p z) (z 0))
|
|
(define (cdr-p z) (z 1))
|
|
#+end_src
|
|
|
|
Zdaj imamo procedure za delanje s pari, ki so definirane brez podatkov.
|
|
Obskurno, ampak v okviru definije delanja s pari. Na tem primeru vidimo, da
|
|
zmoznost manipuliranja procedur kot objektov avtomaticno omogoci moznost za
|
|
reprezentacijo sestavljenih podatkov. (Proceduralna reprezentacija podatkov bo
|
|
igrala osrednjo vlogo v nadaljevanju - temu se rece /message passing/ in bo
|
|
osnovno orodje v tretjem poglavju o problemih modeliranja in simulacije).
|
|
|
|
#+begin_src scheme
|
|
;; naloga 2.5
|
|
|
|
(define (cons-2a3b a b)
|
|
(* (expt 2 a) (expt 3 b))
|
|
)
|
|
(define (car-2a3b x)
|
|
(if (= 0 (modulo x 2))
|
|
(+ 1 (car-2a3b (/ x 2)))
|
|
0
|
|
)
|
|
)
|
|
(define (cdr-2a3b x)
|
|
(if (= 0 (modulo x 3))
|
|
(+ 1 (cdr-2a3b (/ x 3)))
|
|
0
|
|
)
|
|
)
|
|
(cons-2a3b 3 5)
|
|
(car-2a3b (cons-2a3b 3 5))
|
|
(cdr-2a3b (cons-2a3b 3 5))
|
|
;; naloga 2.6 church numerals
|
|
|
|
(define zero (λ (x) x))
|
|
(define (add-1 n) (λ (f) (λ (x) (f ((n f) x)))))
|
|
|
|
(define one (λ (f) (λ (x) (f (f x)))))
|
|
(define two (λ (f) (λ (x) (f (f (f x))))))
|
|
;; pomoje je to narobe, en f prevec je. ampak, ko sva z gizmotom na papir
|
|
;; napisala je bilo kul.
|
|
#+end_src
|
|
|
|
#+RESULTS:
|
|
: #<unspecified>
|
|
|
|
*** razsirjena vaja: aritmetika z intervali
|
|
|
|
#+begin_src scheme
|
|
;; aritmetika z intervali
|
|
|
|
(define (add-interval x y)
|
|
(make-interval (+ (lower-bound x) (lower-bound y))
|
|
(+ (upper-bound x) (upper-bound y))
|
|
)
|
|
)
|
|
|
|
(define (mul-interval x y)
|
|
(let ((p1 (* (lower-bound x) (lower-bound y)))
|
|
(p2 (* (lower-bound x) (upper-bound y)))
|
|
(p3 (* (upper-bound x) (lower-bound y)))
|
|
(p4 (* (upper-bound x) (upper-bound y)))
|
|
)
|
|
(make-interval (min p1 p2 p3 p4) (max p1 p2 p3 p4))
|
|
)
|
|
)
|
|
;; @todo tukaj ne razumem, kako to, da ni p1 vedno najmanjsi in p4 vedno najvecji.
|
|
;; https://en.wikipedia.org/wiki/Interval_arithmetic
|
|
|
|
(define (div-interval x y)
|
|
(mul-interval x
|
|
(make-interval (/ 1.0 (upper-bound y))
|
|
(/ 1.0 (lower-bound y))
|
|
)
|
|
)
|
|
)
|
|
;; naloga 2.7
|
|
(define (make-interval a b) (cons a b))
|
|
(define (upper-bound i) (cdr i))
|
|
(define (lower-bound i) (car i))
|
|
;; naloga 2.8
|
|
(define (sub-interval a b)
|
|
(make-interval (- (lower-bound a) (upper-bound b))
|
|
(- (upper-bound a) (lower-bound b))
|
|
)
|
|
)
|
|
;; od spodnje meje odstejes zgornjo mejo drugega intervala
|
|
|
|
|
|
;; naloga 2.9
|
|
|
|
;; Pokazi, da je sirina intervala (polovica razdalje med zgornjo in spodnjo
|
|
;; mejo),oziroma, da je sirina vsote in razlike intervalov funkcija sirin
|
|
;; argumentov ter da sirina proudkta in deljenja ni funkcija sirine.
|
|
|
|
(define (width a) (/ (- (upper-bound a) (lower-bound a)) 2.0))
|
|
|
|
;; sirina vsote je enaka vsoti sirin, ce razpises je hitro jasno.
|
|
;; sirina razlike je enaka vsoti sirin.
|
|
|
|
;; pri mnozenju imamo funkcijo min in max in ubistvu se nikjer notri ne pojavi
|
|
;; sirina.
|
|
|
|
;; naloga 2.10
|
|
(define (safe-div-interval x y)
|
|
(if (or (= 0 (lower-bound y)) (= 0 (upper-bound y)))
|
|
(error "Division by zero?")
|
|
(div-interval x y)
|
|
)
|
|
)
|
|
|
|
;; naloga 2.11
|
|
|
|
;; S preverjanjem predznakov v mejah intervalov lahko locis mnozenje intervalov
|
|
;; na 9 primermov, od katerih samo eden potrebuje vec kot 2 mnozenji. Razpisi
|
|
;; s to metodo.
|
|
|
|
;; verjetno bi rabil se neko preverjanje, da je nizja meja intervala res nizja.
|
|
|
|
(define (mul-interval-signs x y)
|
|
(cond
|
|
((and (= (test-sign-interval x) 1) (= (test-sign-interval y) 1)) (make-interval (* (lower-bound x) (lower-bound y)) (* (upper-bound x) (upper-bound y)) ))
|
|
((and (= (test-sign-interval x) -1) (= (test-sign-interval y) -1)) (make-interval (* (upper-bound x) (upper-bound y)) (* (lower-bound x) (lower-bound y)) ))
|
|
;; en cel pozit, drug cel negat
|
|
((and (= (test-sign-interval x) -1) (= (test-sign-interval y) 1)) (make-interval (* (lower-bound x) (upper-bound y)) (* (upper-bound x) (lower-bound y)) ))
|
|
((and (= (test-sign-interval x) 1) (= (test-sign-interval y) -1)) (make-interval (* (upper-bound x) (lower-bound y)) (* (lower-bound x) (upper-bound y)) ))
|
|
;; en cez nic, drug cel pozit
|
|
((and (= (test-sign-interval x) 0) (= (test-sign-interval y) 1)) (make-interval (* (lower-bound x) (upper-bound y)) (* (upper-bound x) (upper-bound y)) ))
|
|
((and (= (test-sign-interval x) 1) (= (test-sign-interval y) 0)) (make-interval (* (upper-bound x) (lower-bound y)) (* (upper-bound x) (upper-bound y)) ))
|
|
;; en cez nic, drug cel negat
|
|
((and (= (test-sign-interval x) 0) (= (test-sign-interval y) -1)) (make-interval (* (upper-bound x) (lower-bound y)) (* (lower-bound x) (lower-bound y)) ))
|
|
((and (= (test-sign-interval x) -1) (= (test-sign-interval y) 0)) (make-interval (* (lower-bound x) (upper-bound y)) (* (lower-bound x) (lower-bound y)) ))
|
|
;; oba cez nic, je treba sprobat
|
|
(else (mul-interval x y))
|
|
)
|
|
)
|
|
|
|
;; returns -1 if lower and upper bound are below 0
|
|
;; return 0 if lower bound is below 0 and upper bound is above
|
|
;; return 1 if both bounds are above 0
|
|
(define (test-sign-interval x)
|
|
(cond
|
|
((>= (lower-bound x) 0) 1)
|
|
((< (upper-bound x) 0) -1)
|
|
(else 0)
|
|
)
|
|
)
|
|
|
|
;; test mul intervals - definiram 3 intervale in jih mnozim same s sabo in
|
|
;; drugega z drugim.
|
|
|
|
(define (test-mul-int)
|
|
(let
|
|
((i1 (make-interval 2 3))
|
|
(i2 (make-interval -5 7))
|
|
(i3 (make-interval -13 -11))
|
|
)
|
|
(display i1) (display " * ")(display i1) (display " mul: ") (display (mul-interval i1 i1)) (display " sig: ") (display (mul-interval-signs i1 i1)) (newline)
|
|
(display i2) (display " * ")(display i2) (display " mul: ") (display (mul-interval i2 i2)) (display " sig: ") (display (mul-interval-signs i2 i2)) (newline)
|
|
(display i3) (display " * ")(display i3) (display " mul: ") (display (mul-interval i3 i3)) (display " sig: ") (display (mul-interval-signs i3 i3)) (newline)
|
|
(display i1) (display " * ") (display i2) (display " mul: ") (display (mul-interval i1 i2)) (display " sig: ") (display (mul-interval-signs i1 i2)) (newline)
|
|
|
|
(display i1) (display " * ") (display i3) (display " mul: ") (display (mul-interval i1 i3)) (display " sig: ") (display (mul-interval-signs i1 i3)) (newline)
|
|
|
|
(display i2) (display " * ") (display i3) (display " mul: ") (display (mul-interval i2 i3)) (display " sig: ") (display (mul-interval-signs i2 i3)) (newline)
|
|
(display i3) (display " * ") (display i2) (display " mul: ") (display (mul-interval i3 i2)) (display " sig: ") (display (mul-interval-signs i3 i2)) (newline)
|
|
(display i3) (display " * ") (display i1) (display " mul: ") (display (mul-interval i3 i1)) (display " sig: ") (display (mul-interval-signs i3 i1)) (newline)
|
|
(display i2) (display " * ") (display i1) (display " mul: ") (display (mul-interval i2 i1)) (display " sig: ") (display (mul-interval-signs i2 i1)) (newline)
|
|
)
|
|
)
|
|
|
|
(define (make-center-width c w)
|
|
(make-interval (- c w) (+ c w)))
|
|
|
|
(define (center i)
|
|
(/ (+ (lower-bound i) (upper-bound i)) 2.0))
|
|
|
|
(define (width i)
|
|
(/ (- (upper-bound i) (lower-bound i)) 2.0))
|
|
|
|
;; nalog 2.12
|
|
|
|
(define (make-center-percent c p)
|
|
;; percentage is between 0 and 1
|
|
(if
|
|
(or (< p 0.0) (> p 1.0))
|
|
(error "Percentage should be a decimal number between 0 and 1")
|
|
(make-center-width c (* c p))
|
|
)
|
|
)
|
|
|
|
(define (percent i)
|
|
(/ (width i) (center i))
|
|
)
|
|
|
|
;; naloga 2.13
|
|
|
|
;; za majhne odstotke je preprosta forumalu za priblizek produkta.
|
|
(define (interval-mul-pribl a b)
|
|
(make-center-percent (* (center a) (center b)) (+ (percent a) (percent b)))
|
|
)
|
|
;; nvm ce je to to, ampak za mnozejnje (make-center-percent 1000 0.01) in (mcp
|
|
;; 2000 0.01) dobim za produkt po direktni formuli percent produkta 0.019998, po
|
|
;; moji formuli za priblizek pa 0.02, kar je dost podobno.
|
|
|
|
(define (par1 r1 r2)
|
|
(div-interval (mul-interval r1 r2)
|
|
(add-interval r1 r2)))
|
|
|
|
(define (par2 r1 r2)
|
|
(let ((one (make-interval 1 1)))
|
|
(div-interval one
|
|
(add-interval (div-interval one r1)
|
|
(div-interval one r2)))))
|
|
|
|
;; naloga 2.14
|
|
|
|
;; hja dobimo drugacne rezultate, nvm zakaj...
|
|
|
|
;; naloga 2.15
|
|
|
|
#+end_src
|
|
|
|
** Hierarhični podatki in lastnosti zaprtosti
|
|
|
|
#+begin_center
|
|
Closure Property =>
|
|
https://dis-slovarcek.ijs.si/list_searched
|
|
#+end_center
|
|
|
|
Pare sestavljamo s ~cons~.
|
|
|
|
Sposobnost ustvarjanja/sestavljanja parov, katerih elementi so pari je bistvo
|
|
strukture podatkovnih seznamov kot orodja za reprezentacijo. Temu recemo
|
|
lastnosti zaprtosti. Na splosno, operacija za zdruzevanje podatkov zadosca
|
|
*lastnosti zaprtosti*, ce so lahko rezultati zdruzevanja podatkov tudi sami
|
|
zdruzeni z istimi operacijami.
|
|
/Treba je pazit, ker closure tukaj pride iz abstraktne algebre. Lisp skupnost pa/
|
|
/s closure opisuje tudi nepovezan koncept - implementacijsko tehniko za procedure/
|
|
/s prostimi spremenljivkami. V sicp closure nima tega pomena./
|
|
|
|
Zaprtost je kljuc do sredstev kombinacij, ker omogoca ustvarjanje hierarhicnih
|
|
struktur - struktur, ki so sestavljene iz struktur.
|
|
|
|
*** Reprezentacija sekvenc
|
|
~list~ funkcija je ekvivalentna konsanju argumentov na ~cons~.
|
|
|
|
**** operacije s seznami
|
|
|