Replacing a symbol in a symbolic expression

本秂侑毒 提交于 2019-11-29 18:01:40

The quickest way is to use a flag indicating whether the replacement has already been done, something along the lines of:

(define (context sxp sym)
  (define done #f)
  (let loop ((sxp sxp))
    (cond (done sxp)
          ((pair? sxp) (cons (loop (car sxp)) (loop (cdr sxp))))
          ((eq? sym sxp) (set! done #t) '())
          (else sxp))))

It's not very elegant to use set!, but the alternative would be to have the procedure return 2 values, and the resulting let-values code would be even worse in terms of readability IMO.

Also note that I didn't use atom? because it's not defined in standard Scheme; the usual way is to successively test null? then pair?, and handle the atom case in the else clause.

This is a bit more general (you can replace things other than symbols, and you can customize the test, and you can specify any particular number of instances to replace, not just one), and may be a little bit more complicated at first glance than what you're looking for, but here's a solution that works by internally using a continuation-passing style helper function. The main function, subst-n takes a new element, and old element, a tree, a test, and a count. It replaces the first count occurrences of new (as compared with test) with old (or all, if count is not a non-negative integer).

(define (subst-n new old tree test count)
  (let substs ((tree tree)
               (count count)
               (k (lambda (tree count) tree)))
    (cond
      ;; If count is a number and zero, we've replaced enough
      ;; and can just "return" this tree unchanged.
      ((and (number? count) (zero? count))
       (k tree count))
      ;; If the tree is the old element, then "return" the new
      ;; element, with a decremented count (if count is a number).
      ((test old tree)
       (k new (if (number? count) (- count 1) count)))
      ;; If tree is a pair, then recurse on the left side, 
      ;; with a continuation that will recurse on the right
      ;; side, and then put the sides together.  
      ((pair? tree)
       (substs (car tree) count
               (lambda (left count)
                 (substs (cdr tree) count
                         (lambda (right count)
                           (k (cons left right) count))))))
      ;; Otherwise, there's nothing to do but return this 
      ;; tree with the unchanged count.
      (else
       (k tree count)))))

> (display (subst-n '() 'a '((a . b) . (a . d)) eq? 1))
((() . b) a . d)
> (display (subst-n '() 'a '((a . b) . (a . d)) eq? 2))
((() . b) () . d)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!