Add exercises from section 2.4

This commit is contained in:
Petar Kapriš 2025-09-30 19:46:12 +01:00
parent f318ae0a70
commit b326460d60
4 changed files with 151 additions and 0 deletions

50
chapter-2/ex-2.73.txt Normal file
View file

@ -0,0 +1,50 @@
a) The predicates `number?` and `variable?` can't be assimilated because they
are checking the data type of the datum itself, not the value of a specific
symbol which can be found in a table.
b) Here I'll only write the most basic version of the procedures, since the
more elaborate versions were done in previous exercises.
(define (install-deriv-package)
(define (deriv-sum operands var)
(let ((addend (car operands))
(augend (cadr operands)))
(make-sum (deriv addend var)
(deriv augend var))))
(define (deriv-product operands var)
(let ((multiplicand (car operands))
(multiplier (cadr operands)))
(make-sum
(make-product multiplier
(deriv multiplicand var))
(make-product (deriv multiplier var)
multiplicand))))
(put 'deriv '+ deriv-sum)
(put 'deriv '* deriv-product)
'done)
c) exponential rule:
...
(define (deriv-expon operands var)
(let ((base (car operands))
(exponent (cadr operands)))
(if (constant? exponent var)
(make-product (make-product exponent
(make-exponentiation base
(make-sum
exponent
-1)))
(deriv base var))
(error "non-constant exponents not yet supported: DERIV" exponent))))
...
(put 'deriv '** deriv-expon)
...
d) if the dispatch line looked like this:
((get (operator exp) 'deriv) (operands exp) var)
the only part we would really have to change is the put lines for each of the
functions, like so:
(put 'deriv '+ deriv-sum) -> (put '+ 'deriv deriv-sum)

59
chapter-2/ex-2.74.txt Normal file
View file

@ -0,0 +1,59 @@
This is a txt file because there's no sense in trying to make it into a proper
runnable scheme file, unless I go out of my way to make several examples of
data structure and record formats, complete with examples in order to actually
run these:
a) Before any of the data records in the file itself, there should be an
identifier for what type of record it is, so each file should start with a
symbol, number or string that uniquely identifies the file structure/format,
for example:
european-division-format
(...)
(...)
...
This piece of info should be in the same position in every file, so that a
uniform function, say (get-file-type file) could be used to find the proper
format. If it's not possible to change these division formats at all, then
either this change will be done while loading the file, or, the get-file-type
function will have to be more complicated.
In any case, once we have solved the format marking issue using any kind of
method, the files and the records could be structured any how, as long as each
record has a unique identifier to the employee.
These can then be searched with different functions for different formats, all
of which could be organized in a table, and called by a master function, which
will load the record, and then attach it with a tag, which will make record
processing easier too:
(define (get-record name file)
(let ((file-type (get-file-type file)))
(attach-record-type file-type
((get 'get-record file-type) name))))
We will also assume all these functions return nil if the record doesn't
exist, or a single record if it does.
b) The way that we've written the get-record function, it doesn't matter how a
particular employee record is structured, because we tag it as the respective
file type ourselves, but of course it must contain the salary field, we must
also define a function which gets the record type, and the rest of the record:
get-record-type and get-record-body
(define (get-salary record)
((get 'get-salary (get-record-type record)) (get-record-body record)))
c) We assume there's only one employee for the given name
(define (find-employee-record name files)
(let ((returns (filter (lambda (record) (not (null? record)))
(map get-record files))))
(if (null? returns)
nil
(car returns))))
d) All changes could be done in the new company's (or department's) package,
where they would simply define a new format name, add the format name tag to
their records, modify their get-record and get-salary functions appropriately,
and then add these to the updated global function table.

10
chapter-2/ex-2.75.scm Normal file
View file

@ -0,0 +1,10 @@
#lang sicp
(define (make-from-real-imag r a)
(define (dispatch op)
(cond ((eq? op 'real-part) (* r (cos a)))
((eq? op 'imag-part) (* r (sin a)))
((eq? op 'magnitude) r)
((eq? op 'angle) a)
(else (error "Unknown op: MAKE-FROM-MAG-ANG" op))))
dispatch)

32
chapter-2/ex-2.76.txt Normal file
View file

@ -0,0 +1,32 @@
Explicit dispatch:
New type: A new constructor(s), must be made for the new data type, which
will add the necessary fields and attach a new type tag. Each of the
functions for the type will need to have a new condition line to check for
the new type.
New op: The new op will be written in an explicit dispatch style to process
all of the preexisting types
Data-directed style:
New type: A new package will be written, complete with all the necessary
functions to process the new type, they will then all be added to a new
row in the table
New op: Each existing package will have the op added to it, and will also
add it to a new column in the table
Message-passing style:
New type: A new dispatch function will be created, with all of the lines
which exist in functions of previous types
New op: A new line is added into each of the dispatch functions of the
preexisting types
If new operations are often added, you may prefer the explicit dispatch style,
since it involves only creating/modifying one place in the codebase.
If new types are often added, you may instead prefer one of the other two
styles, since here, an addition of a new type is done in one place in the
code, while adding a new op requires changes in many places.
That being said, the data-directed style has the extra advantage that it could
(in principle) be written in a way that's more organized for adding new
operations, since the table structure itself will work equally well in either
case, and it's more of a matter of code organization.