Scheme 分配律简化

Scheme 分配律简化,scheme,Scheme,我试图编写一个程序,利用代数表达式的分布特性来简化它: (dist '(+ x y (exp x) (* x 5) y (* y 6))) => (+ (* x (+ 1 5)) (* y (+ 1 1 6)) (exp x)) (dist '(+ (* x y) x y)) => (+ (* x (+ y 1)) y) ; or => (+ (* y (+ x 1)) x) 正如第二个示例所示,可能有多个结果,我不需要全部列

我试图编写一个程序,利用代数表达式的分布特性来简化它:

(dist '(+ x y (exp x) (* x 5) y (* y 6)))
=> (+ (* x (+ 1 5))
      (* y (+ 1 1 6))
      (exp x))

(dist '(+ (* x y) x y))
=> (+ (* x (+ y 1))
      y)
; or
=> (+ (* y (+ x 1))
      x)

正如第二个示例所示,可能有多个结果,我不需要全部列举,只需要一个有效的结果。我想知道是否有人能至少为我提供一个定性描述,说明他们将如何着手解决这个问题?谢谢:)

非常通用的方法:

(dist expr var-list)
=> expr factored using terms in var-list
dist
必须了解诸如
+、-、*、/等“可分发”函数以及它们各自的行为。比如说,如果它只知道前四个,那么:

(dist expr var-list
  (if (empty? var-list) expr
    (let* ([new-expr (factor expr (first var-list))])
      (return "(* var " (dist new-expr (rest var-list)))))
“返回”(*var)语法不正确,但您可能已经知道了。我不是racket或lisp专家,但基本上这归结为字符串处理?在任何情况下,
因子
都需要充实,以便它从
*
函数中删除单个
var
,并从
+
函数中删除所有
var
离子(替换为1)。它还需要足够聪明,只有在至少有两个替换时才能这样做(否则我们实际上什么都没有做)。

Oleg Kiselyov's使得跨术语分配因子变得非常容易:

(define dist
  (λ (expr)
    (pmatch expr
      [(* ,factor (+ . ,addends))
        `(+ ,@(map (λ (addend)
                     (list factor addend))
                   addends))]
      [else
        expr])))

(dist '(* 5 (+ x y))) => (+ (5 x) (5 y))
主要的技巧是匹配一个模式,并从模式中相应的插槽中从表达式中提取元素。这需要一个
cond
let
,使用复杂的表达式将
cdr
放到列表中的正确位置,并将
car
从正确的元素中取出。
pmatch
写入
cond
为您服务

分解公共项比较困难,因为您必须查看所有子表达式以找到公共因子,然后将它们提取出来:

(define factor-out-common-factors
  (λ (expr)
    (pmatch expr
      [(+ . ,terms) (guard (for-all (λ (t) (eq? '* (car t)))
                                    terms))
        (let ([commons (common-factors terms)])
          `(* ,@commons (+ ,@(remove-all commons (map cdr terms)))))]
      [else
        expr])))

(define common-factors
  (λ (exprs)
    (let ([exprs (map cdr exprs)]) ; remove * at start of each expr
      (fold-right (λ (factor acc)
                    (if (for-all (λ (e) (member factor e))
                                 exprs)
                        (cons factor acc)
                        acc))
                  '()
                  (uniq (apply append exprs))))))

(define uniq
  (λ (ls)
    (fold-right (λ (x acc)
                  (if (member x acc)
                      acc
                      (cons x acc)))
                '()
                ls)))


(factor-out-common-factors '(+ (* 2 x) (* 2 y)))
=> (* 2 (+ (x) (y)))

输出可以进一步清理,这不包括分解出1,并且
删除所有
缺失,但我将把这一切留给您。

我没有花时间分析第一个示例,但您的第二个示例是“分解”常见的x或y。您是希望这样做,还是“通过分发”?我知道两者都遵循分配定律,但我想答案可能会有所不同,这取决于你在寻找什么。确切地说,我只是想通过“分解”得到一个表达式任何常见因素。这可能是一种更好的解释方法。如果您正在尝试执行我认为您正在尝试执行的操作,您应该使用宏(或者可能是
apply
)而不是字符串处理。