Вместо того, чтобы думать о списках списков, это может быть полезным местом для размышлений в терминах дерева, построенного из пар (также называемого cons-клетками). Ячейка cons - это то, что вы возвращаете, когда звоните (cons x y
), и x
называется его car
, а y
называется его cdr
. Когда вы ищете для элемента e
в каком-то объекте object
, вы можете сделать это следующим образом:
- Is
e
же, как object
? Если это так, вы его нашли!
- (a) Есть
object
пара? Если да, то проверьте, является ли e
(b) в его car
или (c) в его cdr
, и если да, то вы его нашли!
- В противном случае вы не нашли его и его нигде не было.
Это относительно просто перевести в код, и вы даже можете сделать это довольно чистый, используя or
и 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
Теперь, это даже позволяет найти подразделы из дерева в виде дерева , Например, если вы взяли дерево '((1 2) (3 (4 5)) (6 7))
и извлекли его второй элемент (список (3 (4 5))
) и спросите, есть ли его член, вы получите положительный ответ. Обратите внимание, что это работает, потому что вы проверяете с eq?
, поэтому было бы только найти же объект, а не только один с той же структурой:
(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
Однако, поскольку собственно список завершается пустой список (например, (1 2 3)
- (1 . (2 . (3 .())))
), tree-find
всегда будет указывать, что '()
является членом, если на входе имеется правильный список. Таким образом, вы можете явно запретить этот случай:
(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))))))