简化符号表达

时间:2019-10-09 11:15:19

标签: lisp common-lisp

我是Lisp的新手,我需要一些帮助。 我需要简化下一个表达式: 从(+ (+ A B) C)(+ A B C) 并从(- (- A B) C)(- A B C)

如果您可以帮助我解决其中的一个问题,那么我将了解到我需要如何处理下一个问题。

非常感谢。

2 个答案:

答案 0 :(得分:0)

假设您有一个与此模式(+ e1 ... en)匹配的输入,您想将所有e1递归简化为en,这将给您s1,..., sn,然后提取以si开头的所有+,以将其参数向上移动一层,到您要构建的简化表达式。

  • 如果e,则表达式(and (consp e) (eq '+ (car e)))与上述模式匹配。
  • 然后,所有ei仅由(cdr e)列表给出。
  • 考虑(+)的情况,如何简化呢?
  • 要将函数f应用于值列表,请调用(mapcar #'f list)
  • 要基于谓词p将一个列表分为两个列表,可以使用循环:

    (let ((sat nil) (unsat nil))
      (dolist (x list (values sat unsat))
        (if (funcall predicate x) 
            (push x sat) 
            (push x unsat))))
    

    有一种纯粹的功能编写方式,你能弄清楚吗?

答案 1 :(得分:0)

这是用Racket编写的一个简单的简化程序,它为+实现了一个相当简单的简化程序。请注意,这并不意味着什么严重的问题:这只是我在考虑此问题时输入的内容。

这可能以简单的方式使用了Racket的模式匹配来完成某些工作。

(define/match (simplify expression)
  ;; simplifier driver
  (((cons op args))
   ;; An operator with some arguments
   ;; Note that this assumes that the arguments to operators are always
   ;; expressions to simplify, so the recursive level can be here
   (simplify-op op (map simplify args)))
  ((expr)
   ;; anything else
   expr))

(define op-table (make-hash))

(define-syntax-rule (define-op-simplifier (op args) form ...)
  ;; Define a simplifier for op with arguments args
  (hash-set! op-table 'op (λ (args) form ...)))

(define (simplify-op op args)
  ;; Note the slightly arcane fallback: you need to wrap it in a thunk
  ;; so hash-ref does not try to call it.
  ((hash-ref op-table op (thunk (λ (args) (cons op args)))) args))

(define-op-simplifier (+ exprs)
  ;; Simplify (+ ...) by flattening + in its arguments
  (let loop ([ftail exprs]
             [results '()])
    (if (null? ftail)
        `(+ ,@(reverse results))
        (loop (rest ftail)
              (match (first ftail)
                [(cons '+ addends)
                 (append (reverse addends) results)]
                [expr (cons expr results)])))))

有可能比这更具侵略性。例如,我们可以合并文字数字的运行,因此我们可以将(+ 1 2 3 a 4)简化为 (+ 6 a 4)(请注意,除非所有算术都是精确的,否则通常将其进一步简化为(+ 10 a)是不安全的)。这是一个针对+*进行合并的函数:

(define (coalesce-literal-numbers f elts)
  ;; coalesce runs of literal numbers for an operator f.
  ;; This relies on the fact that (f) returns a good identity for f
  ;; (so in particular it returns an exact number).  Thisis true for Racket
  ;; and CL and I think any Lisp worth its salt.
  ;;
  ;; Note that it's important here that (eqv? 1 1.0) is false.
  ;;;
  (define id (f))
  (let loop ([tail elts]
             [accum id]
             [results '()])
    (cond [(null? tail)
           (if (not (eqv? accum id))
               (reverse (cons accum results))
               (reverse results))]
          [(number? (first tail))
           (loop (rest tail)
                 (f accum (first tail))
                 results)]
          [(eqv? accum id)
           (loop (rest tail)
                 accum
                 (cons (first tail) results))]
          [else
           (loop (rest tail)
                 id
                 (list* (first tail) accum results))])))

这是+的修改后的简化程序,使用它。除合并外,它还可以将(+ x)简化为x

(define-op-simplifier (+ exprs)
  ;; Simplify (+ ...) by flattening + in its arguments
  (let loop ([ftail exprs]
             [results '()])
    (if (null? ftail)
        (let ([coalesced (coalesce-literal-numbers + (reverse results))])
          (match coalesced
            [(list something)
             something]
            [exprs
             `(+ ,@exprs)]))
        (loop (rest ftail)
              (match (first ftail)
                [(cons '+ addends)
                 (append (reverse addends) results)]
                [expr (cons expr results)])))))

以下是使用此增强型简化器的示例:

> (simplify 'a)
'a
> (simplify 1)
1
> (simplify '(+ 1 a))
'(+ 1 a)
> (simplify '(+ a (+ b c)))
'(+ a b c)
> (simplify '(+ 1 (+ 3 c) 4))
'(+ 4 c 4)
> (simplify '(+ 1 2 3))
6

要获得更多价值,您会注意到*的简化符实际上是相同的,并将其更改为:

(define (simplify-arith-op op fn exprs)
  (let loop ([ftail exprs]
             [results '()])
    (if (null? ftail)
        (let ([coalesced (coalesce-literal-numbers fn (reverse results))])
          (match coalesced
            [(list something)
             something]
            ['()
             (fn)]
            [exprs
             `(,op ,@exprs)]))
        (loop (rest ftail)
              (match (first ftail)
                [(cons the-op addends)
                 #:when (eqv? the-op op)
                 (append (reverse addends) results)]
                [expr (cons expr results)])))))

(define-op-simplifier (+ exprs)
  (simplify-arith-op '+ + exprs))

(define-op-simplifier (* exprs)
  (simplify-arith-op '* * exprs))

现在

(simplify '(+ a (* 1 2 (+ 4 5)) (* 3 4) 6 (* b)))
'(+ a 36 b)

比较合理。

您可以做得更多,例如,当为一个运算符合并数字时,您可以简单地省略该运算符的身份序列:(* 1 1 a 1 1 b)可以简化为(* a b),而不是{{1} }。这样做似乎很愚蠢:谁会写这样的表达式,但是当简化复杂的表达式时,它们很容易出现。

此代码的详细版本有gist。可能仍然是越野车。