Reputation: 115
I am trying to define a function in Scheme, using Pretty Big language (in Dr. Racket) that will take a list and convert all the 'atoms' to top-level elements. For example, if given:
(level '(a b (c d) (e f (g 4 h))))
;=> (a b c d e f g 4 h)
Here is the code I have so far:
;;level -takes list and returns list w/all elements as top-level
(define level
(lambda (L)
(cond ((null? L) L)
((not( pair? L)) L)
(else (append (level(car L)) (level(cdr L)))))))
My error is as follows:
append: contract violation
expected: list?
given: d
Can anyone help me troubleshoot this error?
Upvotes: 0
Views: 547
Reputation: 70135
Anytime you define a recursive function, every clause ought to return objects of a similar type. In your case, the recursive call in the third clause expects a list to be returned (for use by append
) but the second clause returns an 'atom'. And thus the compiler/runtime complains with 'expected a list.'
The fix for this is to return (list L)
in the second cond
clause.
Upvotes: 0
Reputation: 48745
Append works on lists. If you call level
with a list '(1 2 3)
the very first iteration it will do (append (level '1) (level (cdr '(2 3)))
. Now '1 is no pair thus will evaluate to 1, which is not a list. It will be like calling (append '1 ...)
which is a contract violation.
EDIT
Here is a implementation of flatten
in Pretty Big. This is based on Chris Jester-Young's answer for a similar question. It's more efficient than the append
versions.
(define (flatten lst)
;; helper function that accumulates
(define (reverse-flatten-into x lst)
(if (pair? x)
(foldl reverse-flatten-into lst x)
(cons x lst)))
(reverse (reverse-flatten-into lst '())))
Upvotes: 1
Reputation: 85813
For more about how to implement flatten
(which is what this kind of function is usually called), have a look at
As to your specific error, append
expects all (it can usually take more than two) of its arguments to be lists. E.g.,
> (append '(1 2 3) '(4 5 6))
;=> (1 2 3 4 5 6)
> (append '(1 2 3) '(4 5 6) '(7 8 9))
;=> (1 2 3 4 5 6 7 8 9)
Now, you're writing your function, and you've said that level
should return a list. That means that if level
has a few different execution paths, each one needs to produce a list. So, let's have a look at your implementation.
(define level
(lambda (L)
(cond ((null? L) L)
((not( pair? L)) L)
(else (append (level(car L)) (level(cdr L)))))))
In the question, you said that you're writing a function that should take a list, so L
can be one of two things; it can either be the empty list, or it can be a pair. At the moment, your cond
has three cases, though.
(cond ((null? L) L) ; handle an empty list
((not( pair? L)) L)
(else (append (level(car L)) (level(cdr L))))) ; handle a pair
If you're always calling level
with a list, you don't need the second case. However, since in the third case, you do call (level (car L))
, and you don't know whether or not whether (car L)
will be a list, it seems like you do eventually call level
with non-lists. You need to make a decision about whether, e.g., (level 'a)
should be legal, and if it should, what it should be. At the moment, it seems like you're trying to have (level 'a)
be legal and return (a)
. That's fine, but you should specify the contract. If this is what you want to do, then you do need that second case in your cond
, but since (level 'a)
should return (a)
, you actually need that case to return (list L)
, not L
.
The other option here, if you do want level
to be strict, and always require a list as an argument, then you need to add some more logic to determine whether the (car L)
is a list, and if it is, to recursively call level
on it, and call append
with the result. One way to do this would be like this:
(define (level L)
(cond
((null? L) L)
((pair? L) (append (if (list? (car L))
(level (car L))
(list L))
(level (cdr L))))))
Upvotes: 2