At the moment, as part of an assignment, I am attempting to write a scheme program that accepts 3 messages, 'get-student-average 'get-assignment-average and 'add-grade, and returns the information as necessary. 'add-grade basically gives the student id, assignment, and grade as it is inserted in that order. I am still working on the student and assignment average messages (hence the semicolons in front of them), but what keeps failing when I try to run it is the 'add-grade message. It keeps saying that grades is an unbound variable. This is the code that I am testing it with:
(define (make-grades)
(define T '())
(define (dispatch op)
((eq? op 'add-grade) (lambda(id as gr) (set! T (append T (list (list id as gr)
)))))
;((eq? op 'get-student-average) (lambda(x) ( )))
;((eq? op 'get-assignment-average)
))
(define grades (make-grades))
((grades 'add-grade) 7 1 85))
I'm not sure what it is that I am doing wrong to try and get back the information with that. I thought that, as with tables in scheme, the trick is in appending the list as a list to a null value, then setting it.
There are a bunch of other test cases (including some for the 'average messages), but I did not include them as I feel that it's just repeating more of the same. The one other thing I feel I should mention is that all of the test-cases are held together within a list, starting with '( and ending with ).
There are a couple of problems with your code. For starters, you are not actually checking what message is being received (there should be a cond or a series of nested ifs or a case somewhere). And you're not returning the dispatcher procedure. Here, try this to get started:
(define (make-grades)
(define T '())
(define (dispatch op)
(cond ((eq? op 'add-grade)
(lambda (id as gr)
(set! T (append T (list (list id as gr))))))
;((eq? op 'get-student-average) (lambda(x) ( )))
;((eq? op 'get-assignment-average)
))
dispatch)
Also, you should add a fourth message to return the list, for testing purposes.
Related
I need help with one issue: I can't handle how to get the first atom from the list in SCHEME language. I think I should make a loop, something like "while" and compare each element on boolean (?atom) and print first matched item.
Unfortunately, It's difficult for me to handle scheme syntax.
Please, can you propose any solution for me?
define func ?atom:
(define (atom? x) (not (or (pair? x) (null? x))))
Recursion is not that different from the usual way yo solve problems - you just have to be careful and set some meaningful base cases. For instance, how would you solve this problem in Java? by traversing all the list, and stoping when either 1) we found the element that matches the condition or 2) there are no more elements. This is exactly what we need to do:
(define (first-atom lst)
(cond ((null? lst) #f) ; there are no more elements in the list
((atom? (car lst)) ; current element is an atom
(car lst)) ; return it
(else ; otherwise
(first-atom (cdr lst))))) ; look in the rest of the list
It works as expected:
(first-atom '((2) 42 (3)))
=> 42
(first-atom '((1) (2)(3)))
=> #f
In your question you have the definition to atom? that returns #t if the argument is an atom and #f otherwise.
The function should handle the empty list. eg. What should happen when you do this:
(first-atom '())
Thus you need to check if the argument is the empty list and return whatever you supposed to return when there are no atoms in the arguments. Then you'll have a else expression that handles the rest.
You can use first to get the first element to check if it is an atom and then return it or you recure with the rest of the list.
Now here is something very similar that finds the number of elements in a list:
(define (length lst)
(if (null? lst)
0 ; obviously a 0 length list
(+ 1 (length (cdr lst))))) ; obviously one plus whatever length the rest is
Imagine what happens if you do (length '(1 2)). It does (+ 1 (length '(2))) which again does (+ 1 (+ 1 (length '()))) which again does (+ 1 (+ 1 0)). Simple as that. All loops are recursive functions in Scheme.
You reference to while indicates you might be familiar with other programming languages. I knew C, C++, Pascal, perl, PHP, and Java when starting to learn Lisp and I suddenly realized all the languages I knew were only subtle dialects of one language, Algol. I wasn't learning my sixth language, but my second. Scheme doesn't have a while loop. It has recursion. you need to get a book and start at the first page as if you didn't know how to program at all as assimilation from Algol will point you in the wrong direction.
I am trying to learn typed scheme/racket(?). Down below I have an example from my code:
#lang typed/racket
(: add (Real Real -> Real))
(define (add x y)
(+ x y))
I would like to implement a procedure "check" which checks if two datatypes are allowed with an operator. For example,
(check '(+ int int))
Should result in
int
But
(check '(* int (+ real int)))
Should result in something like this:
The operator '+' must have two operands of the same (numerical) type.
That is, check should take a list.
Questions:
How do I implement "check"? Firstly I though "ok, I have a list, so let's use car and cdr" to get the operator and the operands but it didn't work and I don't even know why it doesn't work. I also though about making if statements like (if (and (= x y) (or (= x int) (= y int)) and so on to make the checks but... don't think this is the right way to go.
Should I make a procedure "add" or not? Is there any other way to do this? Int the examples it looks like they are only using "+", "-" and so on. Lastly; How do I check that the input "int" is an int and then gives int as output.
I am pretty lost right now and I am sorry for my pretty vaugue questions but I would be really happy if someone could help me out to understand this.
Note: the procedure add takes real numbers and output a real number so it doesn't follow along with the example too well. But I hope you grasp the idea. Thanks :)
You're asking a fascinating question, and it doesn't have a simple answer.
The program that you're trying to write is essentially a type-checker. That is, it takes an expression, and checks to see whether the given function's domain includes the arguments it's being called with. We can write one of those, but I suspect you're going to be unsatisfied. Here, let me go write one now....
#lang typed/racket
(require typed/rackunit)
;; a type is either
;; - 'number, or
;; - (list 'fn list-of-types type)
;; examples of types
'number
'(fn (number number) number)
(define-type FnTy (List 'fn (Listof Ty) Ty))
(define-type Ty (U 'number FnTy))
;; given an expression, returns a type
;; or signals an error
(: check (Any -> Ty))
(define (check input)
(cond [(and (list? input)
(pair? input)
(symbol? (car input)))
(define fn-ty (lookup-fn-type (car input)))
(define arg-types (map check (rest input)))
(cond [(equal? (cadr fn-ty) arg-types)
(caddr fn-ty)]
[else (error 'check
"expression didn't type-check: ~v\n"
input)])]
[(number? input)
'number]
[else (raise-argument-error
'check
"well-formed expression"
0 input)]))
(: lookup-fn-type (Symbol -> FnTy))
(define (lookup-fn-type fn-name)
(match fn-name
['+ '(fn (number number) number)]
[other (raise-argument-error 'lookup-fn-type
"known function name"
0 fn-name)]))
(define TEST-INPUT '(+ 3 43))
(check-equal? (check TEST-INPUT)
'number)
(check-equal? (check '(+ (+ 3 4) 129837))
'number)
Does this answer any part of your question?
In Chapter 3 of The Little Schemer, the answer to the question of why we don't simplify the rember function right away is "because then a function's structure does not coincide with its argument's structure." I'm having trouble understanding what a function's structure is, what an argument's structure is, and what the difference is between them.
Here's the unsimplified version:
(define rember
(lambda (a lat)
(cond
((null? lat) (quote ()))
(else (cond
(( eq? (car lat) a) (cdr lat))
(else (cons (car lat)
(rember a
( cdr lat)))))))))
And here's the simplified:
(define rember
(lambda (a lat)
(cond
((null? lat) (quote ()))
((eq? (car lat) a) (cdr lat))
(else (cons (car lat)
(rember a (cdr lat)))))))
From what I can tell, the main difference is that the function has gone from two conds asking one question each to one cond asking two questions.
The function's arguments are the atom "a" and the list "lat."
This is the first time, outside of the densely written foreword, where the book references the word "structure." In my mind, the definition of the word "structure" so far is open to interpretation.
Someone has asked this exact question here before, but I have trouble following the answer. Why does a two-cond structure coincide or not coincide with the structure of a list? A list, in my mind, doesn't have any conditions at all!
Isn't a condition equivalent to a question in Scheme? Perhaps I'm misunderstanding what a condition is, which could be a reasonable root of my frustration. Anyways, any clarification on this would be very much appreciated! Thank you!
Here for “structure of function” the author probably means that the body of the function, that is the condition:
(cond
((null? lat) ...)
(else ... (cond... (car lat) ... (cdr lat) ...)))
patterns exactly the “recursive” definition of a list, as either:
an empty list, or
a value with at least one element (the car), and a list (the cdr).
The new definition instead “folds” the two cond inside a single one, so that the “structure” of the function (the structure of the cond) does not reflect any more the “structure” of its list argument.
List is a type that could have been defined, with some pseudocode, as
(define-variant-record list
( () ) ; '() is a list
((hd . tl) ; a cons pair (with car field named `hd` and cdr `tl`)
(list tl)) ) ; is a list, if `tl` itself is a list
Then, it could be handled with a hypothetical (cf. EOPL) patern-matching construct variant-case:
(define rember
(lambda (a lat) ; an atom and a list of atoms
(variant-case (lat) ; follow the `lat` --
( () ; an empty list case, or
(quote ()))
( (hd . tl) ; a non-empty list, with car field `hd` and cdr `tl`
(cond
(( eq? hd a) tl)
(else
(cons hd
(rember a tl))))))))
which, by way of using the variant-case belonging to the data-type definition of list, naturally and visibly follows its structure (i.e. the two cases of its definition).
The first formulation (with the nested conds) just emulates the non-existent variant-case with the explicit access to the concrete data-type implementation, with the cars and the cdrs as it does.
The inner cond does not belong to this, and just deals with the specifics of rember on its own. That's why mashing the two conds into one may be seen as mixing the non-related concerns into one mish-mash (generally speaking; although here both are extremely simple and clear).
I am trying to write a program which will add numbers in a list. However, when I give the input as a list, Scheme does not give me an output.
My code is the following:
(define (sumlist lst)
(cond ( (pair? lst) (+ (car lst) (sumlist(cdr lst))) )))
Why does this happen? I am giving input properly, i.e, I am quoting the list.
I am giving input as follows:
(sumlist '(1 2 3))
EDIT:
I changed the question slightly. The list was not quoted in pair? 'lst and that is why I was getting an error.
Now, I am not getting an error. However, I am not getting any output either.
EDIT2:
I unquoted the list in pair? lst. However, now it is giving me the following error: Wrong type in arg2 #
I have updated the code accordingly.
Your function application syntax is wrong. Function application is always prefix in Scheme, i.e. car(lst) should be (car lst), etc.
Also, (pair? 'lst) is wrong, since you should not be quoting the argument. That will test if the symbol lst is a pair, which is obviously always false.
You need a base case for when you don't want to recurse—when you receive the empty list—which should return 0.
Putting all these together, and you should have this:
(define (sumlist lst)
(if (pair? lst)
(+ (car lst) (sumlist (cdr lst)))
0))
(I also changed cond to if since cond is unnecessary in this case.)
In a Computer Science course I am taking, for homework, we were tasked with several different questions all pertaining to message passing. I have been able to solve all but one, which asks for the following:
Write a mailman object factory (make-mailman) that takes in no parameters and
returns a message-passing object that responds to the following messages:
'add-to-route: return a procedure that takes in an arbitrary number of mailbox objects
and adds them to the mailman object's “route”
'collect-letters: return a procedure that takes in an arbitrary number of letter objects and
collects them for future distribution
'distribute: add each of the collected letters to the mailbox on the mailman's route whose
address matches the letter's destination and return a list of any letters whose destinations
did not match any mailboxes on the route (Note: After each passing of
'distribute
the
mailman object should have no collected letters.)
Some remarks that are given to make the code easier include:
If multiple letters are distributed to the same mailbox in one distribution round, any one
of them may be the “latest” letter whose message is returned by passing 'get-latest-message
to the mailbox.
No two mailboxes will have the same address.
No mailbox or letter will be passed to the mailman more than once.
The bad letters returned by distribute do not need to be in a specific order.
Use the . args syntax for accepting arbitrary amount of arguments.
This is what I have been able to figure out for myself:
(define (make-mailman)
(let ((T '()))
(define (route-adder . mobjects)
(assoc mobjects T))
(define (letter-collecter . lobjects)
(assoc lobjects T))
(define (add-to-route mobjects)
(begin (set! T (cons (route-adder . mobjects) T)) 'done))
(define (collect-letters lobjects)
(begin (set! T (cons (sort-strings (letter-collecter . lobjects)) T)) 'done))
(define (dispatch z)
(cond ((eq? z 'add-to-route) add-to-route)
((eq? z 'collect-letters) collect-letters)
((eq? z 'distribute) "unsure of what to do here")
(else "Invalid option")))
dispatch))
Any help that can be given to me here will be appreciated, as I have tried looking at this problem for a while, and cannot figure out what to do from here.
Your code has all kinds of mix-ups. :) Let's proceed step by step.
The dispatch bit is almost OK:
(define (make-mailman)
(let ...
...
(define (dispatch msg) ;; use short but suggestive var names
(cond
((eq? msg 'add-to-route) add-to-route)
((eq? msg 'collect-letters) collect-letters)
((eq? msg 'distribute)
;; "unsure of what to do here" <<-- Distribute the letters, what else?
distribute-the-letters)
(else "Invalid option")))
dispatch))
With such objects, a sample call will be (define ob (make-mailman)) and then ((ob 'add-to-route) box1 box2 ... boxn) etc. So add-to-route procedure must be defined this way:
(define (make-mailman)
(let ((self (list '(ROUTE) ; each mailman has a route, and a mailbag
'(MAILBAG)))) ; use suggestive name here (T, what T?)
...
(define (add-to-route . mailboxes)
(let ((route (assoc 'ROUTE self)))
(set-cdr! route
(append mailboxes ; there will be no duplicates
(cdr route)))
'DONE))
Right? Same with the letters:
(define (collect-letters . letters)
(let ((mailbag (assoc 'MAILBAG self)))
.....
'DONE))
Now we can deal with the missing part, distribute-the-letters:
(define (distribute-the-letters)
;; for each letter in my mailbag
(let* ((mailbag (assoc 'MAILBAG self))
(mailboxes (cdr (assoc 'ROUTE self)))
(letters (cdr mailbag)))
(if (null? letters) ()
(let loop ((letter (car letters))
(letters (cdr letters))
(not-delivered ()))
;; access its address,
(let* ((address (letter 'get-address))
;; (we assume it supports this interface,
;; or maybe that's part of a previous assignment)
;; and find a mailbox on my route such that
(mbx (find-mailbox address mailboxes)))
;; its address matches the letter's
;; and if so,
(if .....
;; put that letter into this mailbox:
((mbx 'put-letter) letter)
;; (we assume it supports this interface,
;; or maybe that's part of a previous assignment)
;; but if not, add letter to the "not-delivered" list
..... )
(if (null? letters)
;; having emptied the mailbag, return the "not-delivered" list
(begin (set-cdr! mailbag nil) not-delivered)
(loop (car letters) (cdr letters) not-delivered)))))))
We assume that both letter and mailbox objects support the message type 'get-address to which they both return the same comparable address type of object, and that mailbox objects support 'put-letter message.
Other than the specifics of the message functionality, it looks like you've nailed it. There are however some errors:
This (route-adder . mobjects) should be (router-adder objects) and similarly for (letter-collector . lobjects).
The use of begin is unneeded. The body of a (define (func . args) <body> ...) is implicitly enclosed in a begin.
Idiomatically your code could be written as:
(define (make-mailman)
(let ((T '()))
;; ...
(lambda (z)
(case z
((add-to-route) add-to-route)
((collect-letters) collect-letters)
((distribute) distribute)
(else (error "Invalid option"))))))
[but you may not know about case nor lambda yet...]
As for solving the actual messaging functionality. You are going to need to maintain a set of mailboxes where each mailbox is going to hold a set of letters. A letter will presumably consist of an address and some content (extra credit for a return-address). The distribute behavior will check the address on each letter and deposit it in its mailbox. The mailman will need to hold letters (while on his route collecting-letters) until instructed to distribute.
For this you might start by building up the lower-levels of the functionality and then using the lower-levels to build up the actual message passing functionality. Starting like, for example:
(define (make-letter addr content)
`(LETTER ,addr ,content))
(define letter-addr cadr)
;; ...
(define (make-mailbox addr)
'(MBOX ,addr))
(define mailbox-letters cddr)
(define (mailbox-letters-add mailbox letter)
(set-cdr! (cdr mailbox) (cons letter (mailbox-letters mailbox))))
;;...