繁体   English   中英

方案列表中的替换值

[英]Substitute values in Scheme lists

假设我有一个代表 Boolean 表达式的任意列表:

'(a and (b or c))和另一个表示文字值的对列表

'((a. #t) (b. #f) (c. #t)))

我想要做的是使用第二个列表中的值替换第一个列表中的值,并返回一个新列表:

'(#t and (#f or #t))

因此,例如,这段代码采用一个列表和一个关联列表,并成功地替换了其中的值,尽管我试图在给定一个conses列表而不是一个关联列表的情况下做同样的事情。 它适用于'(ab) '((a 1) (b 2)) -> '(1 2) ,但我想改为使用'((a. 1) (b. 2)) 这是代码:

(define replace
    (lambda (ls a-list)
     (map (lambda (x)
            (let ((lookup (assq x a-list)))
              (if lookup
                  (cadr lookup)
                  x)))
          ls)))

有人知道怎么做吗? 非常感谢!

像这样的东西:

(define bool '(a and (b or c)))
(define pairs '((a . #t) (b . #f) (c . #t)))

(define (value-for-key lst key)
  (cond ((empty? lst) 'none)
        ((eq? key (caar lst))
         (cdar lst))
        (#t (value-for-key (cdr lst) key))))

(define (replace-bool lst vals)
  (cond ((empty? lst) lst)
        ((pair? (car lst)) (cons (replace-bool (car lst) vals)
                                 (replace-bool (cdr lst) vals)))
        (#t (let ((val-for-key (value-for-key vals (car lst))))
                (if (not (eq? val-for-key 'none))
                    (cons val-for-key (replace-bool (cdr lst) vals))
                    (cons (car lst) (replace-bool (cdr lst) vals)))))))

(replace-bool bool pairs)

-> (#t 和 (#f 或 #t))

第一个 function 用于在键值对列表中搜索并返回给定键的值或“无”,如果该键不在该列表中。
第二个 function 通过 Boolean 表达式列表。 在键值列表中搜索每个原子元素,如果找到,则替换。 如果 Boolean 表达式包含嵌套表达式,则递归遍历它们。

这里真正的问题似乎是需要一个适用于嵌套输入列表的解决方案。 为此,可以编写一个map-nested过程。 这是一个类似于通常map过程的简化版本的过程,但它适用于嵌套列表:

(define (map-nested proc xss)
  (if (null? xss)
      '()
      (let ((xs (car xss)))
        (if (pair? xs)
            (cons (map-nested proc xs)
                  (map-nested proc (cdr xss)))
            (cons (proc xs)
                  (map-nested proc (cdr xss)))))))

这与通常的map不同的一种方式是map-nested仅将一个列表作为输入; 一个典型的map过程采用可以映射的任意数量的列表。

上面的map-nested实现不是尾递归的,这对于大型、深度嵌套的列表可能是个问题。 将其重写为迭代(尾递归)过程并不麻烦:

(define (map-nested proc xss)
  (let iter ((xss xss)
             (result '()))
    (if (null? xss)
        (reverse result)
        (let ((xs (car xss)))
          (if (pair? xs)
              (iter (cdr xss)
                    (cons (map-nested proc xs) result))
              (iter (cdr xss)
                    (cons (proc xs) result)))))))

这里的result是一个累加器,用于跟踪要返回的结果,从而使递归调用无需保留堆栈帧。 请注意,在返回之前必须反转result ,因为输入中的元素从输入列表的前面开始被 consed 到result的前面。 这里使用了一个命名的 let形式; 这是在另一个 function 中设置本地程序的便捷方式,该程序在 Scheme 和 Racket 中都可以使用。

要完成解决OP的问题,我们只需要使用map-nested 上述任何一个版本都可以工作:

;;; `substs` is an a-list of the form `((a . x) (b . y) ...)`
(define (replace-elts xss substs)
  (map-nested (lambda (x)
                (let ((subst-pair (assoc x substs)))
                  (if subst-pair        ; subst-pair is #f if x is not found
                      (cdr subst-pair)  ; otherwise, subst-pair is a dotted pair
                      x)))
              xss))

在这里, map-nested负责处理输入列表,无论它是否嵌套。 proc的过程负责查找substs中的替换,假定它是由点对组成的关联列表。 如果需要两个元素的正确列表而不是点对,则可以简单地将cdr更改为cadr subst不提供对来自输入xss的值的替换时,该值保持不变。

根据实际使用条件,可以通过多种方式将replace-elts更改为或多或少方便。

示例交互:

nested-map.rkt> (replace-elts '(a (b (c d) e))
                              '((a . 1) (b . 2) (c . 3) (d . 4) (e . 5)))
'(1 (2 (3 4) 5))

nested-map.rkt> (replace-elts '(a (b (c d) e))
                              '((a . 1) (b . 2) (c . 3) (d . 4)))
'(1 (2 (3 4) e))

nested-map.rkt> (replace-elts '(a and (b or c))
                              '((a . #t) (b . #f) (c . #t)))
'(#t and (#f or #t))

有一个 Common Lisp function, sublis正是这样做的,但它与其他处理 cons 单元而不是直接列表的树结构的 CL 函数一样,从未进入 Scheme 世界:

* (sublis '((a . t) (b . nil) (c . t)) '(a and (b or c)))
(T AND (NIL OR T))

前阵子我写了一些 Common Lisp 函数的 Racket 实现,包括这个。

(define (sublis alist tree #:test [test eqv?] #:key [key identity])
  (cond
    ((assoc (key tree) alist test) => cdr)
    ((pair? tree)
     (let ([new-car (sublis alist (car tree) #:test test #:key key)]
           [new-cdr (sublis alist (cdr tree) #:test test #:key key)])
       (if (and (eqv? (car tree) new-car)
                (eqv? (cdr tree) new-cdr))
           tree
           (cons new-car new-cdr))))
    (else tree)))

例子:

> (sublis '((a . #t) (b . #f) (c . #t)) '(a and (b or c)))
'(#t and (#f or #t))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM