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
)而不是字符串处理。