I need a procedure which takes a list and checks if an element is part of that list even when the list contains lists. So far, I\'ve written this:
(define (elem
Rather than thinking in terms of lists of lists, this may be a useful place to think in terms of a tree built from pairs (also known as cons cells). A cons cell is what you get back when you call (cons x y
), and x
is called its car
, and y
is called its cdr
. When you're searching for an element e
in some object object
, you can do it this way:
e
the same as object
? If so, you've found it!object
a pair? If so, then check whether e
is (b) in its car
or (c) in its cdr
, and if so, then you've found it!This is relatively straightforward to translate into code, and you can even make it pretty clean using or
and and
:
(define (tree-find e object)
(or (eq? e object) ; 1.
(and (pair? object) ; 2.a
(or (tree-find e (car object)) ; 2.b
(tree-find e (cdr object)))))) ; 2.c
Now, this even allows you to find subparts of a tree within a tree. For instance, if you took the tree '((1 2) (3 (4 5)) (6 7))
and extracted its second element (the list (3 (4 5))
), and ask whether its a member, you'll get an affirmative answer. Note that this works because you're checking with eq?
, so it would only find the same object, not just one with the same structure:
(let* ((l '((1 2) (3 (4 5)) (6 7)))
(e1 (cadr l)) ; (3 (4 5)), from l
(e2 (list 3 '(4 5)))) ; (3 (4 5)), but not from l
(display (tree-find e1 l)) ; #t
(display (tree-find e2 l))) ; #f
However, because a proper list is terminated by an empty list (e.g., (1 2 3)
is (1 . (2 . (3 . ())))
), tree-find
will always say that '()
is a member if there's a proper list in the input. As such, you might want to explicitly disallow that case:
(define (tree-find e object)
(or (and (eq? e object)
(not (null? e))) ; check only non-null objects
(and (pair? object)
(or (tree-find e (car object))
(tree-find e (cdr object))))))
You're not recurring down the car
of your set
argument.
If you evaluate
(element-of-set? 3 '(a (a b b (c b) 3) 5 5 (e s) (s e s)))
with your current definition of element-of-set?
, it'll do something like
(eq? 3 'a)
, nope.(eq? 3 '(a b b (c b) 3))
, nope.(eq? 3 5)
, nope(eq? 3 5)
, nope(eq? 3 '(e s))
, nope(eq? 3 '(s e s))
, nope.3
is not a member of '(a (a b b (c b) 3) 5 5 (e s) (s e s))
If you want to check for deep set membership, you need to redefine your procedure so that it recurs into the car
of set
if that element is itself a list
. Something like
...
((list? (car set)) (or (element-of-set? element (car set))
(element-of-set? element (cdr set))))
...
should do it, assuming list?
is actually defined (I don't have a scheme interpreter on this machine, so I'm not sure. You may need to use (not (atom? (car set)))
instead).
You just need another clause to check if the car
is a list. Add this to your cond
(after the eq?
clause):
((list? (car set))
(or (element-of-set? element (car set))
(element-of-set? element (cdr set))))
This recurses on any sublists.