我正在尝试编写一个利用代数表达式的分布属性来简化它的过程:
(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)
正如第二个例子所示,可能有多个可能的结果,我不需要全部枚举它们,只是一个有效的结果。我想知道是否有人能为我提供至少一个关于如何开始攻击这个问题的定性描述?谢谢:))
答案 0 :(得分:2)
Oleg Kiselyov的pmatch macro使得跨越术语分配一个因素非常简单:
(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
和let
。
考虑常用术语更难,因为你必须查看所有子表达式以找出共同因素,然后将它们拉出来:
(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,并且remove-all
缺失,但我会把所有这些都留给你。
答案 1 :(得分:0)
一种非常通用的方法:
(dist expr var-list)
=> expr factored using terms in var-list
dist
必须了解“可分发”函数,例如+,-,*,/,etc
以及每个函数的行为方式。如果,例如,它只知道前四个,那么:
(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“”语法不正确,但你可能已经知道了。无论如何我都不是球拍或lisp专家,但基本上这归结为字符串处理?无论如何, factor
需要充实,以便从var
函数中移除一个*
,从var
函数中删除所有+
(将其替换为1)它还需要足够智能,只有在至少有两个替换时才会这样做(否则我们实际上没有做任何事情)。