General memoization in Scheme - scheme

i have been assigned homework to make a general memoization procedure in scheme, so far it works on procedures that take one argument, but fail on what it seems to be the last argument when provided with more than 1. It also fails to memoize procedures that take no arguments.
Any help would be greatly appreciated.
(define mem
(lambda (mem-it func)
(let ((table (make-table) )(func-store func))
(cond
((equal? mem-it 'memoize)
(lambda args
(if (null? args)
func
(let ((prev (lookup args table)))
(or prev
(let ((result (apply func args)))
(insert! args result table)
result))))))
((equal? mem-it 'unmemoize)
(func-store))
(else (display "No Such command"))))))
This is what i have so far
(define (test-proc . args)
(display "computing test-proc of ")
(display args)
(newline)
(if (null? args)
0
(+ (expt (- 42 (car args)) 2)
(apply test-proc (cdr args)))))
And here is the test procedure provided
The error occurs when i try to run the following test
(set! test-proc (mem 'memoize test-proc))
(test-proc 40 41 42 43 44)
Here are the other procedures used
(define (make-table)
(list '*table*))
(define (lookup key table)
(let ((record (assoc key (cdr table))))
(and record (cdr record))))
(define (insert! key value table)
(let ((record (assoc key (cdr table))))
(if record
(set-cdr! record value)
(set-cdr! table
(cons (cons key value) (cdr table))))))

Your memoizarion procedure has a feature where it returns the implementation procedure when no arguments are passed:
((mem 'memoize test-proc)) ; ==> test-proc
The base case of your test procedure will never hit because of this feature thus for (test-proc 1) you can substitute it with the expression (+ 1681 test-proc) which will signal an error since test-proc is not a number.
It's better to use unique magic values:
(define +GET-PROC+ (list "get-proc"))
(test-proc +GET-PROC+) ; ==> original-test-proc
Since we are making a list it's is eq? that data only. In R6RS you can refrain from exporting so that code that uses memoization doesn't really have access to mess with it. All lists that look like it eg ("get-proc") won't be eq? so it can be used as an argument without getting the original procedure.
Since you are not using a standard hash procedure from (rnrs hashtables) or SRFI-69 it's not possible for me to check it but since you are using a list as key your hashtable must use equal? as test. This is often a source of frustration when using hash tables in most lisps.

Related

Create lists from lists

I want to write a function which for n arguments will create n lists and each contains n-th element for every argument, for example:
(aux '(1 2) '(3 4)) = `((1 3) (2 4))
I wrote such a function:
(define (aux . args)
(if (null? args)
'()
(cons (map car args)
(aux (map cdr args)))))
but when I try to evalute (aux '(1 2) '(3 4)) the REPL does not show any output.
My question is what should I change because I don't see any syntax errors.
Chris is correct. In the event you want to use rest arguments and then use it in recursion you should consider wrapping it in a named let or make a local helper procedure.
(define (zip . args)
(let aux ((args args))
(if (ormap null? args)
'()
(cons (map car args)
(aux (map cdr args))))))
I also do this when there are arguments that don't change. eg. a map implementation for only one list I don't pass the procedure at each iteration:
(define (map1 proc lst)
(let aux ((lst lst))
(if (null? lst)
'()
(cons (proc (car lst))
(aux (cdr lst))))))
Of course what actually will happen is up to the implementation so don't think of any of these as optimizations. It's mostly for code clarity.
You forgot to write an apply in your function. Don't worry, I make this mistake all the time, which was why I spotted it instantly. ;-)
Basically, you need to use (apply aux (map cdr args)). Otherwise, your aux is being recursed into with one argument only.
Oh, and you also need to use (ormap null? args) instead of just (null? args), since the base case is that all your given lists are exhausted, not that you have no given lists.

Is it possible to reimplement "apply" in Scheme?

If I encounter a primitive procedure, do I always use the underlying scheme apply?
Assuming I do so, how would I re-implement apply for the scheme interpreter to interpret itself?
(define apply-1
(lambda (proc args)
(cond ((primitive? proc)
(apply proc args)) <-- How would I reimplement this
((eq? (car proc) 'closure)
(eval (cadr (cadr proc))
(bind (car (cdr proc)) args (caddr proc))))
(else error))))
Primitive-apply is the glue between how a primitive in your interpreter is implemented with the underlying implementation. Using hosts apply to apply primitives that are indeed procedures in the host system is a trick. You cannot make a host apply but you can make a interpreter primitive-apply differently that does less or supports other ways to package primitives. Eg.
;; define representations for primitives
(define prim-cons (list 'cons)) ; system unique
(define prim-car (list 'car))
...
;; define primitive?
(define (primitive? proc)
(or (eq? proc prim-cons)
(eq? proc prim-car)
...))
;; define primitive apply
(define (primitive-apply proc args)
(cond ((eq? proc prim-cons) args)
((eq? proc prim-car) (caar args))
...))
;; boot environment
(define primitive-environment
(list (cons #t prim-true)
(cons #f prim-false)
(cons '() prim-null)
(cons 'cons prim-cons)
(cond 'car prim-car)
...))
The fact is using apply is just a simplification since the actual primitive procedure is the resolved object. It doesn't always have to be like that. Imagine we try to optimize it a little:
;; define representations for primitives
(define prim-cons (list 'cons)) ; system unique
(define prim-car (list 'car))
;; make a list of primitives and their implementation
(define primitives
(list (cons prim-cons values)
(cons prim-car caar)))
;; define primitive?
(define (primitive? proc)
(assq proc primitives))
;; make apply-primitive
(define (apply-primitive proc args)
((cdr (primitive? proc)) args))
Still lot of boilerplate.. Why not move the whole primitive-list into the environment.
;; make tags
(define *primitive* (list 'primitive))
(define *constant* (list 'constant))
;; make a list of primitives and their implementation
(define boot-env
(list (list* 'cons *primitive* values)
(list* 'cons *primitive* caar)
...
(list* #f *constant* #f)
(list* #t *constant* #t)))
;; verify type
(define (is-type? x type)
(and (pair? proc)
(eq? (car proc) type)))
;; define primitive?
(define (primitive? proc)
(is-type proc *primitive*))
(define (constant? x)
(is-type x *constant*))
;; make apply-primitive
(define (apply-primitive proc args)
((cdr proc) args))
Now. For compound procedures we just have similar tag. eval itself become very small since you can even have *special-form* in your environment that does something similar making your eval just a case analysis between the types of values you eval and not special cases.
One of my thought about apply was that I wanted my apply to be the one called when you call the procedure apply from the interpreter. You can make use of apply, but apply actually needs to be handled by apply as well. You'll meet the same weird thing when you try to apply eval too.

Scheme Argument Checker Error-handling

I'm fairly new to Scheme programming and was wondering how I can add some error checkers in this program. I would like it to check if the user types in more than one parameter and if the user does I would like it to say that its an error.
(define (thirds lst)
(cond ((or (null? lst) (null? (cdr lst))) lst)
((null? (cddr lst)) (list (car lst)))
(else (cons (car lst)
(thirds (cdddr lst))))))
The Scheme interpreter should check this automatically. You only need to do your own checking of the number of arguments if you define the procedure to take spread arguments, i.e.
(define (thirds . args)
...)
You would normally only do this if the procedure takes a variable number of arguments. For procedures with static arguments, just list them in the definition and let the interpreter do the checking for you.
If you really want to detect this yourself, you can do:
(define (thirds . args)
(if (= (length args) 1)
(let ((lst (car args)))
(cond ... ; all the rest of your code
))
(display "Oh that's an error")))
So, using your definition of thirds in #!racket (the language) and trying to use it like this:
(thirds '(a b c) '(d e f))
thirds: arity mismatch;
the expected number of arguments does not match the given number
expected: 1
given: 2
arguments...:
'(a b c)
'(d e f)
context...:
/usr/share/racket/collects/racket/private/misc.rkt:87:7
As you can see all computation stops since I have given a one argument procedure two arguments. It's a contract violation and it throws an exception.
It's perfectly possible to make handlers:
(with-handlers ([exn:fail:contract?
(λ (e) (displayln "got a contract error"))])
(thirds '(1 2 3) '(4 5 6)))
; prints "got a contract error"

How Do For Loops Work In Scheme?

I'm having some difficulty understanding how for loops work in scheme. In particular this code runs but I don't know why
(define (bubblesort alist)
;; this is straightforward
(define (swap-pass alist)
(if (eq? (length alist) 1)
alist
(let ((fst (car alist)) (scnd (cadr alist)) (rest (cddr alist)))
(if (> fst scnd)
(cons scnd (swap-pass (cons fst rest)))
(cons fst (swap-pass (cons scnd rest)))))))
; this is mysterious--what does the 'for' in the next line do?
(let for ((times (length alist))
(val alist))
(if (> times 1)
(for (- times 1) (swap-pass val))
(swap-pass val))))
I can't figure out what the (let for (( is supposed to do here, and the for expression in the second to last line is also a bit off putting--I've had the interpreter complain that for only takes a single argument, but here it appears to take two.
Any thoughts on what's going on here?
That's not a for loop, that's a named let. What it does is create a function called for, then call that; the "looping" behavior is caused by recursion in the function. Calling the function loop is more idiomatic, btw. E.g.
(let loop ((times 10))
(if (= times 0)
(display "stopped")
(begin (display "still looping...")
(loop (- times 1)))))
gets expanded to something like
(letrec ((loop (lambda (times)
(if (= times 0)
(display "stopped")
(begin (display "still looping...")
(loop (- times 1)))))))
(loop 10))
This isn't actually using a for language feature but just using a variation of let that allows you to easily write recursive functions. See this documentation on let (it's the second form on there).
What's going on is that this let form binds the name it's passed (in this case for) to a procedure with the given argument list (times and val) and calls it with the initial values. Uses of the bound name in the body are recursive calls.
Bottom line: the for isn't significant here. It's just a name. You could rename it to foo and it would still work. Racket does have actual for loops that you can read about here.

the difference between if and cond?

i'm learning sicp now and do the ex2.23
i have wrirten the following code:
(define (for-each proc items)
(if (null? items)
#t
((proc (car items))
(for-each proc (cdr items)))))
but when running, cause error: procedure application: expected procedure, given: #; arguments were: ()
i think i know the reason: I call the for-each function recursively, every called for-each wanted to return value
but when i have modified the code:
(define (for-each proc items)
(cond ((null? items) #t)
(else (proc (car items)) (for-each proc (cdr items)))))
it runs well. I don't understand, why? in cond, does every called for-each no need to return value?
i used DrScheme, and choose language SICP
i'm not a native speaker of english, so if there is sth which isn't described clearly, pls tell me
but when running, cause error: procedure application: expected > procedure, given: #; arguments were: ()
i think i know the reason: I call the for-each function recursively, > every called for-each wanted to return value
No, it is because in the alternative clause of if you have the combination ((proc (car items)) (for-each proc (cdr items))). You intended to evaluate the two combinations (proc (car items)) and (for-each proc (cdr items)) sequentially, and to that end you thought putting them in another pair of parentheses would work. But in actuality, what you have specified is that the result of (proc (car items)) is a procedure to be applied to the argument which is the return value of (for-each proc (cdr items)). This is not the case, and you get an error. The key point being that parentheses in Lisp are not for grouping, but have a definite significance.
The problem is that if can only have a single combination in that position, whereas you want to have two in a row. On the other hand, cond does not suffer such a restriction; you can put as long a sequence of individual combinations in the consequent part of a cond clause as your heart desires. This state of affairs is simply how the language is defined to work.
You can just as well use cond in these situations, but if you still want to use if there are some options for stuffing multiple combinations into one. E. g. you can create a lambda procedure whose body is the two combinations and immediately fire it off:
(define (for-each proc items)
(if (null? items)
#t
((lambda ()
(proc (car items))
(for-each proc (cdr items)) )) ))
Or you can use begin which is actually meant to be used for such a purpose:
(define (for-each proc items)
(if (null? items)
#t
(begin
(proc (car items))
(for-each proc (cdr items)) ) ))

Resources