user2109706
user2109706

Reputation: 115

Scheme (Pretty Big) defining a "level" funciton

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

Answers (3)

GoZoner
GoZoner

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

Sylwester
Sylwester

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

Joshua Taylor
Joshua Taylor

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

Related Questions