1
votes

I'm trying to write a procedure in Scheme (R5RS) of my CS class that takes an expression (either a symbol or a list) as an argument and returns a list of (1) all the possible expression that can be formed by using car and cdr on the expression and (2) and an expression demonstrating how each of these components of the original expression were obtained. If a piece can be obtained in more than one way, it should be returned more than once.

Examples

(pieces '()) => ((() x))

(pieces 'apple) =>  ((apple x))

(pieces '(apple)) => (((apple) x) (apple (car x)) (() (cdr x)))

(pieces '(a (b c))) =>
               (((a (b c)) x)
                (a (car x))
                (((b c)) (cdr x))
                ((b c) (car (cdr x)))
                (b (car (car (cdr x))))
                ((c) (cdr (car (cdr x))))
                (c (car (cdr (car (cdr x)))))
                (() (cdr (cdr (car (cdr x)))))
                (() (cdr (cdr x))))

Since we've just started with Scheme, we're limited to fairly basic syntax for this assignment. Here's what I have so far:

(define pieces
  (lambda (exp)
    (cond
      ((symbol? exp)
       (list exp 'x))
      ((null? exp)
       (list '() 'x))
      ((list? exp)
       (let ((b (pieces (car exp))) (c (pieces (cdr exp))))
          (list exp 'x b c))))))

(pieces '()) => (() x)

(pieces 'apple) => (apple x)

(pieces '(apple)) => ((apple) x (apple x) (() x))

(pieces '(a (b c))) => ((a (b c)) x (a x) (((b c)) x ((b c) x (b x) ((c) x (c x) (() x)))
                       (() x)))

The procedure returns all of the proper elements, but each recursion causes the components to be nested in an additional list. Is there any way to prevent that?

Also, I have no idea where to start for the second part of the problem (showing how each element was obtained from the original using car and cdr). I've tried a million different approaches, and none of them have even been close to working. If anyone has any hints or suggestions on how to implement that feature, I'd really appreciate it. Thanks a ton.

2
Sounds like you want all the atomic elements (i.e. anything accessible by a chain of cars and cdrs), so a flatten() operation should give the result you're looking for.Raymond Hettinger

2 Answers

1
votes
(pieces 'apple) => (apple x)

But it should be ((apple x)), right? You should get a list where the first and only element is the list (apple x).

The cond clauses that terminate recursion (exp is a symbol or null) return items that should go in the list, while the clause that recurs on car and cdr attempts to create a list of items. As pieces can return both items and lists of items it's kind of hard to make a list of items out of the values that are returned from it: when you do (list exp 'x b c) you don't know if b and c are items that should go into the list or lists of items.

If you make sure that pieces always returns a list of items (e.g. (list (list exp 'x))) it gets a lot easier. When you recur on car and cdr you want to do something like append the lists a and b and add the "current" ((list exp 'x)) item to that list (maybe with cons or something).

For the second part, pieces must know how it got to the current item. You can make pieces take a the "path" to the current item as a (maybe optional) parameter. If the path is a list, then when you call pieces on (car exp) you can add a car symbol to the path you're sending as argument, and for (cdr exp) you can add the symbol cdr. And then you use the path to create something nice to substitute for 'x in (list exp 'x).

0
votes

I know it's not Scheme, but maybe looking at similar language would help. I did this more to practice myself, so take it with a drip of salt, yet it seems to be doing exactly what you're after:

(defun parse-list (whatever)
  (labels ((pieces (expression &optional path)
         (cond
           ((null expression)
        `((,path . nil)))
           ((listp expression)
        (append (list
             `(,path . ,expression))
            (pieces (car expression)
                (cons 'car path))
            (pieces (cdr expression)
                (cons 'cdr path))))
           (t `((,path . ,expression))))))
    (dolist (output (pieces whatever))
      (format t "path ~a => result ~a~&"
          (car output) (cdr output)))))

(parse-list '(a (b c)))

Which then produces this output:

path NIL => result (A (B C))
path (CAR) => result A
path (CDR) => result ((B C))
path (CAR CDR) => result (B C)
path (CAR CAR CDR) => result B
path (CDR CAR CDR) => result (C)
path (CAR CDR CAR CDR) => result C
path (CDR CDR CAR CDR) => result NIL
path (CDR CDR) => result NIL

Sorry, I couldn't get SO's code formatting better than this :)