sicp/zapiski/sicp-lio.org

1108 lines
32 KiB
Org Mode
Raw Normal View History

2024-04-30 19:18:58 +02:00
#+TITLE: Structure and Interpretation of Computer Programs
#+AUTHOR: Lio Novelli
* Foreword and Preface
2024-06-10 01:04:10 +02:00
:PROPERTIES:
:UNNUMBERED: t
:END:
2024-04-30 19:18:58 +02:00
#+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
2024-06-10 01:04:10 +02:00
* Grajenje abstrakcij s procedurami
2024-04-30 19:18:58 +02:00
** 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.
2024-08-21 21:38:05 +02:00
** Sestavljene procedure
2024-04-30 19:18:58 +02:00
- Š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)))~
2024-08-21 21:38:05 +02:00
** Substitucijski model za izvajanje procedur
2024-04-30 19:18:58 +02:00
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
2024-05-16 22:33:30 +02:00
** 1.2.2 Drevesna rekurzija
** 1.2.3 Redi rasti
** 1.2.4 Eksponentna funkcija
2024-06-10 01:04:10 +02:00
Tukaj se naucimu successive squaring, ki potem se veckrat prav pride.
2024-05-29 21:54:05 +02:00
#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
2024-08-21 21:38:05 +02:00
(+ a (slow-multi a (- b 1)))
2024-05-29 21:54:05 +02:00
)
)
(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
2024-08-21 21:38:05 +02:00
;; Russian paesant method - zelo star algoritem.
2024-05-29 21:54:05 +02:00
(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)))
)
)
2024-06-10 01:04:10 +02:00
;; 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
2024-05-29 21:54:05 +02:00
#+end_src
2024-08-21 21:38:05 +02:00
#+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~.
2024-05-16 22:33:30 +02:00
2024-08-21 21:38:05 +02:00
*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
2024-05-16 22:33:30 +02:00
** 1.3 Sestavljanje abstrakcij s procedurami visjega reda
2024-05-29 21:54:05 +02:00
Procedure, ki spreminjajo druge procedure se imenujejo *procedure višjega reda*.
2024-05-16 22:33:30 +02:00
** 1.3.1 Procedure kot argumenti
2024-05-29 21:54:05 +02:00
Primer vsote.
#+begin_src guile
#+end_src
2024-05-16 22:33:30 +02:00
//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))
2024-08-21 21:38:05 +02:00
(simpson-gizmo cube 1 2 0.0001)
2024-05-16 22:33:30 +02:00
)
#+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
2024-05-29 21:54:05 +02:00
// 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)
)
2024-05-29 21:54:05 +02:00
#+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
2024-05-16 22:33:30 +02:00
** 1.3.3 Procedure kot splosne metode
2024-05-29 21:54:05 +02:00
Ce pogledamo proceduro za integral, vidimo mocnejse abstrakcije: procedure, ki
izrazajo splosne racunske metode, neodvisne od posameznih vkljucenih funkcij.
2024-05-16 22:33:30 +02:00
** 1.3.4 Procedure kot vrnjene vrednosti
2024-05-29 21:54:05 +02:00
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).
2024-06-10 01:04:10 +02:00
* 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)
)
)
2024-08-21 21:38:05 +02:00
(define (make-rat-norm-gcd n d)
(let ((g (gcd n d)))
(make-rat-norm (/ n g) (/ d g))
)
)
2024-06-10 01:04:10 +02:00
#+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
2024-08-21 21:38:05 +02:00
- ~make-rat~, ~numer~, ~denom~
2024-06-10 01:04:10 +02:00
- 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))))
)
2024-08-21 21:38:05 +02:00
(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))
)
2024-06-10 01:04:10 +02:00
#+end_src
2024-08-21 21:38:05 +02:00
#+RESULTS:
: #<unspecified>
2024-06-10 01:04:10 +02:00
*** Kaj so podatki?
Pri racionalnih stevilih imamo se en pogoj:
~(/ (numer x) (denom x)) = n/d~
2024-08-21 21:38:05 +02:00
Selektorji, konstruktorji in pogoji tvorijo veljavno reprezentacijo.
2024-06-10 01:04:10 +02:00
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).
2024-08-21 21:38:05 +02:00
#+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