Scheme Function Maker - scheme

I've been working on this procedure in Scheme (specifically Pretty Big) and I've hit a brick wall. What I'm trying to do is create a procedure that can create other procedures or functions. For the most part I think I'm on the correct track.
(define (function-maker function-specs)
(let* [(specs (map cdr function-specs))
(name (caar specs))
(args (cadr function-specs))
(body (cdr(cdr function-specs)))]
(eval(list 'define name
(list 'lambda args body)))))
Current Output:
Above is the current output and I'll explain to the best of my knowledge what is happening. The input takes in three arguments essentially. A name, to name the function. Arguments for the function and finally the body of the function.
The three arguments are then listed together and then it's supposed to create a new function. The picture above shows the error that I'm reaching. Thanks for the help!

To see what's happening, get rid of the eval and examine the list:
(define (function-maker function-specs)
(let* [(specs (map cdr function-specs))
(name (caar specs))
(args (cadr function-specs))
(body (cdr(cdr function-specs)))]
(list 'define name
(list 'lambda args body))))
> (function-maker '((name: add5) (x) (+ x 5)))
'(define add5 (lambda (x) ((+ x 5))))
As you can see, there are too many parentheses in the body, so you're attempting to use the resulting number as a procedure.
You want
(body (car (cdr (cdr function-specs))))
or
(body (caddr function-specs))

Related

User input to a list

I'm trying to take in user input and add it to a list but I have not been able to get it working. I'm still new to scheme and have been browsing around to try to figure it out but I haven't had any luck.
(display "Continue to enter numbers until satisfied then enter e to end")
(newline)
(define (intlist number)
(define number(read-line))
(cond (number? number)
(cons lst (number))
(else
(display lst)
done')))
this is what I have so far. Any help or direction to where I can learn a bit more is appreciated.
Your solution is almost correct, but it doesn't work, because:
Variable lst doesn't exist and with this expression (number), you are calling some undefined function number.
done' is badly written 'done.
Function cons expects element as first argument and other element or list as second argument.
See these examples:
> (cons 1 2)
'(1 . 2)
> (cons 1 '())
'(1)
> (cons 1 (cons 2 (cons 3 '())))
'(1 2 3)
Last example is important here- your function will be recursive and it will return a cons cell in each step. If I will follow your solution, this can be enough:
(define (list-from-user)
(let ((number (read)))
(if (number? number)
(cons number (list-from-user))
'())))
(Note that I used read instead of read-line, because read-line returns string, and let instead of define.)
If you really want to wait for e, you must decide, what happens if user enters something that isn't number and isn't e- maybe just ignore it?
(define (list-from-user)
(let ((user-input (read)))
(cond ((number? user-input) (cons user-input (list-from-user)))
((eq? user-input 'e) '())
(else (list-from-user)))))
Then just add some wrapping function with output:
(define (my-fn)
(begin (display "Continue to enter numbers until satisfied then enter e to end")
(newline)
(list-from-user)))
and call it
> (my-fn)
Note that my function returns list with numbers, instead of some useless 'done, so I can use that function in other functions.
(define (sum-of-list)
(let ((lst (my-fn)))
(format "Sum of given list is ~a." (apply + lst))))
> (sum-of-list)

Structure And Interpretation of Computer Programs Ex2_74

I'm trying to tag a list so that the file1 variable out like the following in Racket:
(Div1 ((Sam Parnell 100) (Tom Edward 1000) (Rob Hanbury 500) (Andy Springwood 500)))
however I am getting:
((Sam Parnell 100) (Tom Edward 1000) (Rob Hanbury 500) (Andy Springwood 500))
I am using "tag" internally in my package to tag a series of records using the make-file procedure. It should follow the evaluation model set out in section 1.1.3 I think, but it feels like it isn't and this may be to do with the fact that I have defined make-file to take an arbitrary number of arguments which may effect the evaluation model in ways I haven't yet understood. I've also attempted to debug this in Racket to no avail as it skips how it is evaluated as I step through, so I'm stuck. If anyone could help, it would be greatly appreciated.
#lang sicp
(#%require (only racket/base error))
(#%require (only racket/base make-hash))
(#%require (only racket/base hash-set!))
(#%require (only racket/base hash-ref))
; table set up
(define *op-table* (make-hash))
(define (put op type proc)
(hash-set! *op-table* (list op type) proc))
(define (get op type)
(hash-ref *op-table* (list op type) '()))
; data tagging set up
(define (attach-tag type-tag contents)
(cons type-tag contents))
(define (type-tag datum)
(if (pair? datum)
(car datum)
(error "Bad tagged datum -- TYPE-TAG" datum)))
(define (contents datum)
(if (pair? datum)
(cdr datum)
(error "Bad tagged datum -- CONTENTS" datum)))
(define (apply-generic op . args)
(let ((type-tags (map type-tag args)))
(let ((proc (get op type-tags)))
(if proc
(apply proc (map contents args))
(error
"No method for these types -- APPLY-GENERERIC"
(list op type-tags))))))
(define (install-Div1-Package)
(define (get-name record)
(car record))
(define (get-address record)
(cadr record))
(define (get-salary record)
(caddr record))
(define (make-record name address salary)
(list name address salary))
(define (get-record key file)
(cond ((null? file) (error "Employee not in file"))
((eq? key (get-name (car file)))
(car file))
(else (get-record key (cdr file)))))
(define (make-file . records)
records)
;interface to the rest of the system
(define (tag x) (attach-tag 'Div1 x))
(put 'get-name '(Div1) get-name)
(put 'get-address '(Div1) get-address)
(put 'get-salary '(Div1) get-salary)
(put 'make-record 'Div1
(lambda (name address salary)
(make-record name address salary)))
(put 'get-record '(Div1) get-record)
(put 'make-file 'Div1
(lambda args
(tag (make-file args)))))
(install-Div1-Package)
(define (make-record name address salary)
((get 'make-record 'Div1) name address salary))
(define record1 (make-record 'Sam 'Parnell 100))
(define record2 (make-record 'Tom 'Edward 1000))
(define record3 (make-record 'Rob 'Hanbury 500))
(define record4 (make-record 'Andy 'Springwood 500))
record1
(define (make-file . records)
(get 'make-file 'Div1) records)
(define file1 (make-file record1 record2 record3 record4))
file1
You forgot to actually call the make-file procedure:
(define (make-file . records)
(get 'make-file 'Div1) ; retrieves the procedure, does nothing with it
records) ; return the same input list
Also, because you want to take an arbitrary number of arguments you need to apply it; this should work:
(define (make-file . records)
(apply (get 'make-file 'Div1) records))

General memoization in 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.

application: not a procedure

I'm in the process of learning Scheme. During an exercise (trying to find a specific value in a registry). When running this bit of code, I get the error message:
application: not a procedure;
expected a procedure that can be applied to arguments
given: (342 "Bike" piece 250)
arguments...: [none]
The code in question:
(define get-post
(lambda (post varunr)
(define find-post
(lambda (post)
(cond (null? post) (display "Errormessage-For-User")
(member varunr (car (car post)))(car post)
(else (find-post (cdr post))))))
find-post (post))) ;; <--- Here's the error message
Thanks for any help you can provide.
Try this:
(define get-post
(lambda (post varunr)
(define find-post
(lambda (post)
(cond ((null? post) (display "Errormessage-For-User"))
((member varunr (car (car post))) (car post))
(else (find-post (cdr post))))))
(find-post post)))
Remember: in Scheme functions are called like this: (f x), not like this: f(x). And in a cond expression, each pair of condition/expression must be surrounded by (). You have to be careful where you put those parentheses, use your IDE's syntax highlighting and formatting capabilities to avoid errors like these.

I have got this code to remove the last element of a list. How can i change this to remove a set word regardless of its location?

#lang racket
(define (remove-last lst)
(if (null? (cdr lst))
'()
(cons (car lst) (remove-last (cdr lst)))))
(remove-last '(Help Me Please))
This then prints out:
(Help Me)
How can I change this? For example if I wanted to remove me.
Like this, for example:
(define (remove-words lst words)
(cond
((null? lst)
'())
((member (car lst) words)
(remove-words (cdr lst) words))
(else
(cons (car lst) (remove-words (cdr lst) words)))))
then
> (remove-words '(Help Me Please) '(Me Help Not))
'(Please)
You can also use the procedures for sets:
(define (remove-words lst words)
(set-subtract lst words))
Please note that you are working with symbols here, not strings.
You can also solve the problem using filter-not and member together:
(define (remove-words lst words)
(filter-not (lambda (x) (member x words) lst))
If you want to cut down on the wordiness of that anonymous function, the tools most suited to that are curry and cut (with the latter needing a (require srfi/26) to use).
Currying a function turns it into a new function that accepts one argument, then returns another function that accepts one more argument, then again, then again, and so on until it has all the arguments it needs to call the original function. A curried version of remove-words would be called as ((remove-words lst) words) instead, and you could make it from our current implementation with (curry remove-words). This requires that the arguments be supplied in left to right order, which doesn't work for member. There's another form curryr that goes right-to-left, but because member takes an optional third argument it won't work.
You could use cut (from srfi 26) then, which lets you pick which arguments of a function you want to "lock-in" and which you want the new function to accept. (cut member <> words)creates a new function (lambda (arg) (member arg words)) and (cut * <> <> 8) creates (lambda (arg1 arg2) (* arg1 arg2 8)). So with this, remove-words looks like:
(require srfi/26)
(define (remove-words lst words)
(filter-not (cut member <> words) lst))
Although going with set-subtract is still probably the best solution since you should avoid reinventing the wheel as much as possible (unless you're trying to learn more about wheels). Nonetheless, it's very useful to have a firm grip on the general functions provided by Racket that make your life easier.

Resources