如何为球拍中有效的符号微分函数修复幂函数的简化函数(使结果更易于阅读)? [英] How to fix my simplification function for power rule (to make results easier to read) for a working Symbolic differentiation function in Racket?

查看:112
本文介绍了如何为球拍中有效的符号微分函数修复幂函数的简化函数(使结果更易于阅读)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的区分功能,可以正常工作.

#lang racket

(define (diff x expr) 
  (if (not (list? expr))
      (if (equal? x expr) 1 0) 
  (let ( (operation (car expr)) 
         (u (cadr expr))
         (v (caddr expr)))
       (case operation
          ((+) (list '+ (diff x u) (diff x v))) 
          ((-) (list '- (diff x u) (diff x v))) 
          ((*) (list '+                             
                     (list '* u (diff x v))
                     (list '* v (diff x u))))       
          ((/) (list '/ (list '- (list '* v (diff x u)) (list '* u (diff x v))) 
                     (list '* v v)))                
          ((^) (list '* v (list '* (list '^ u (- v 1)) (diff x u ))))                           
))))

现在我也使大多数简化功能正常工作,但是某处存在问题,我认为这是我的功率规则简化器

(define(simplify expr)
  (if (not (list? expr)) expr
  (let ((operation (car expr))      
       (a (simplify (cadr expr)))   
       (b (simplify (caddr expr)))) 
   (case operation 
        ((+) (if (and (number? a)(= a 0)) b    
                 (if (number? b) (if (= b 0) a 
                                     (+ a b)) 
                 (list operation a b)))) 

        ((-) (if (and (number? a) (= a 0)) (- b)         
                 (if (number? b) (if (= b 0) a  
                                     (- a b))
                 (list operation a b)))) 

        ((*) (cond [(number? a)
                    (cond [(= 1 a) b]
                          [(= 0 a) 0]
                          [else (if (number? b)
                                    (cond [(= b 1) a]
                                          [(= b 0) 0]
                                          [else (* a b)])
                                    (list operation a b))])]
                   [(and (number? b) (= b 0)) 0]
                   [(list operation a b)]))
;The case a/b where b=1 is currently only simplified if a is number. Insert an extra case into the cond expression handling b=1
        ((/) (cond [(number? a)
                    (cond [(= 1 b) a]
                          [(= 0 a) 0]
                          [else (if (number? b)
                                    (cond [(= b 1) a]
                                          [(= b 0) 0]
                                          [else (/ a b)])
                                    (list operation a b))])]
                   [(and (number? b) (= b 0)) 0]; this is where an error should be thrown
                         (cond [(= b 1) 1]
                   [(list operation a b)]))

       ((^) (cond [(number? a)
                   ;if a is 1, 1^x is always 1
                   (cond [(= a 1) 1]
                         [else (if (number? b)
                                   ;if a and b are 0 throw error else anything ^0 is 1.
                                   (cond [(= b 0) (if (= a 0) (error "A and B are both 0, statement undefined!") 1)]
                                         ;if b is 1, x^1 is always x
                                         [(= b 1) a]
                                         ;else a^b
                                         [(expt a b)])
                                   ;a or b are continuations
                                   (list operation a b))])]                                  
                   [else (list operation a b)]))
 ))))

我已经进行了很多测试并且大多数通过了测试,但是有一些没有通过,我不知道为什么.

(simplify '(/ x 1)) ;why is this not working correctly
(simplify '(+ (* (^ x 5) 0) (* 3 (* 5 (* (^ x 4) 1)))) ;not simplifying correctly
(simplify '(* 3 (* (^ x 2) 1))) ;not simplifying correctly
;(simplify '(/ 1 0));not working 
;(simplify '(^ 0 0));this works fine just returns an exception

解决方案

更短的解决方案

通过使用Racket的内在能力来计算数字(并进行正确的错误消息传递,例如除以零错误)

#lang racket

(define (simplify expr)
  (if (not (list? expr)) 
      expr
      (cond ((= 1 (length expr)) (simplify (car expr)))
            ((= 2 (length expr))
             (let ((operation (car expr))
                   (a (simplify (cadr expr))))
               (case operation
                 ((-) (cond ((number? a) (- a))
                            (else (list operation (simplify a)))))
                 ((+) (cond ((number? a) a)
                            (else (simplify a))))
                 (else (error "Diadic operator with only one argument given.")))))
            ((= 3 (length expr))        
             (let ((operation (car expr))        
                   (a (simplify (cadr expr)))     
                   (b (simplify (caddr expr))))   
               (case operation  
                 ((+) (cond ((and (number? a) (number? b)) (+ a b)) 
                            ((number? a) 
                             (cond ((zero? a) (simplify b)) 
                                   (else (list operation (simplify a) (simplify b))))) 
                            ((number? b) 
                             (cond ((zero? b) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))
                 ((-) (cond ((and (number? a) (number? b)) (- a b)) ;; use Racket's ability to subtract
                            ((number? a)
                             (cond ((zero? a) (- (simplify b)))
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))
                 ((*) (cond ((and (number? a) (number? b)) (* a b)) ;; use Racket's ability to mulitpy
                            ((number? a)
                             (cond ((zero? a) 0)
                                   ((= a 1) (simplify b))
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) 0)
                                   ((= b 1) (simplify a))
                                   (else (list operation (simplify a)(simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))
                 ((/) (cond ((and (number? a) (number? b)) (/ a b)) ;; use Racket's ability to divide
                            ((number? a)
                             (cond ((zero? a) 0)
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) (error "Divison by 0, statement undefined!"))
                                   ((= b 1) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else
                             (list operation (simplify a) (simplify b)))))
                 ((^) (cond ((and (number? a) (number? b)) (expt a b)) ;; use Racket's ability to exponentiate
                            ((number? a)
                             (cond ((= a 1) 1)  ;; ((zero? a) 0) ;; depends on b [b < 0 then undefined]
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) 1) ;; depends on a [a = 0 then undefined]
                                   ((= b 1) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))))))))

对于某些仅以数字和运算符结尾的示例, 它有助于多次应用simplify.例如. (simplify (simplify (simplify '(+ (+ (* 2 1) (* x 0)) 0))))返回2.

简短解决方案的说明

开头,if子句测试表达式是否为列表.(not (list? expr)).如果不是,则表示表达式是数字或变量符号.因此,我们将它们返回(expr). 否则,我们知道expr由以下形式组成:一个列表 (<operator - one of */-+^> <first-operand - called a here> <second-operand 'b'>). 我们通过cond子句测试列表的长度. 如果长度为1 (= 1 (length expr)),则意味着设置了一些多余的括号,例如((+ 1 2))(1),因此我们返回(car expr)以使表达式摆脱多余的括号. 如果列表的长度为2,那么我们可以处理(+ 3) => 3之类的情况 或此类多余的加号.或更复杂的表达式(+ (* 3 4)).因此,在所有情况下,我们都对结果递归调用simplify.

如果表达式的长度为3,我们将解析操作,第一个操作数a和第二个操作数b.然后,根据运营商,我们要遵循不同的简化规则. 但是,这些指令具有所有相似的模式.

;; each of the case branches has the pattern:
((<operator>) (cond ((and (number? a) (number? b)) (<operator> a b))
                    ((number? a)
                    ...)
                    ((number? b)
                    ...)
                    (else
                      (list operation (simplify a) (simplify b)))))

条件为(and (number? a) (number? b))的第一个子句涉及两个操作数均为数字的情况.这种情况很容易处理,因为Racket已经知道"如何处理基本的数学运算.因此,您只需计算该值,然后将其作为简化返回(因此,将(<operator> a b)作为返回值). 注意:对于/,如果b为零,则必须引发Division by zero错误.但是,如果我们给b,赋零,那么Racket会自动提高它,所以在这里我们只让Racket也进行错误提高,然后让它返回并计算Racket (/ a b).

第二个子句是a是数字的情况. 由于第一次测试必须失败,因此我们现在可以肯定:ab中的至少一个(如果不是两者)都是组合形式或符号而不是数字.如果满足此条件,则我们知道a是一个数字,但是b必须组成. (如果b是数字,则第一个条件应为true ...). 作为说明,我们再次具有cond with子句. 这些子句将a的特殊情况作为数字处理. 加法+和减法-都对0不变,因此这里两个分支的第一个检查是a是否为零(zero? a).在这种情况下,我们省略运算符和a=0,仅返回b.但是由于b肯定是组合形式,因此我们在其上调用simplify以递归地简化b(否则,将按原样给出表达式b,而无需进一步简化). 如果a不为零,我们将到达else子句,知道a是一个非零数字,并且b必须是一个组合形式. 因此,我们列出了运算符,并且每个ab都简化了. 实际上,a不需要进一步简化,因为我们知道,它是一个数字.您可以删除a周围的simplify调用,而只需编写a. 对于*/,我们区分了3种情况:

  • 如果*/a为零,那么我们知道一切都变为零,因此对于整个表达式,我们在此时返回0.
  • 然后,我们要求*/的不变量,即1.因此,(= 1 a)因此,在这种情况下,我们返回(simplify b)而不包含a和运算符.

对于第三个子句,如果b是一个数字且由a组成,则所有内容都与上一个子句相同-只是将b替换为a.而且只有一个例外:对于/,如果b为零,则必须给出division by zero错误.因此,我们在这里通过(error "Divison by 0, statement undefined!")引发错误.由于a不是数字,因此我们无法让Racket在此处计算任何内容,因此我们会人工引发错误.

仅当ab本身组成时,才会调用第四子句(else),因为此路径中的所有先前测试均失败. 在这种情况下,我们递归调用(simplify a)(simplify b)并将这些结果与运算符一起列出.因此,对于每个操作数,将采用此处描述的简化过程.

重要: ab的let绑定也会在操作数上调用simplify.如果我们在这里省略simplify调用,则简化将在一级之后停止.因此,如前一天所见,simplify调用(在let绑定中的调用)和在cond-分支下游的调用都必须通过整个表达式树拉"递归.昨天,当我在cond子句中忘记了simplify时;).因此,这两个simplify调用层就像马达一样,将简化过程一直拉到底部...

解决方案

(define (simplify expr)
  (if (not (list? expr))
      expr
      (let ((operation (car expr))
            (a (cadr expr))
            (b (caddr expr)))
        (case operation
          ((+) (cond ((and (number? a) (number? b))
                      (cond ((zero? a) b)
                            ((zero? b) a)
                            (else (+ a b))))
                     (else (list operation (simplify a) (simplify b)))))
          ((-) (cond ((and (number? a) (number? b))
                      (cond ((zero? a) (- b))
                            ((zero? b) a)
                            (else (- a b))))
                     (else (list operation (simplify a) (simplify b)))))
          ((*) (cond ((and (number? a) (number? b))
                      (cond ((or (zero? a) (zero? b)) 0)
                            ((= a 1) b)
                            ((= b 1) a)
                            (else (* a b))))
                     ((number? a)
                      (cond ((zero? a) 0)
                            ((= a 1) (simplify b))
                            (else (list operation (simplify a) (simplify b)))))
                     ((number? b)
                      (cond ((zero? b) 0)
                            ((= b 1) (simplify a))
                            (else (list operation (simplify a)(simplify b)))))
                     (else (list operation (simplify a) (simplify b)))))
          ((/) (cond ((and (number? a) (number? b))
                      (cond ((zero? b) (error "Divison by 0, statement undefined!"))
                            ((zero? a) 0)
                            ((= b 1) a)
                            (else (/ a b))))
                     ((number? a)
                      (cond ((zero? a) 0)
                            (else (list operation (simplify a) (simplify b)))))
                     ((number? b)
                      (cond ((zero? b) (error "Divison by 0, statement undefined!"))
                            ((= b 1) (simplify a))
                            (else (list operation (simplify a) (simplify b)))))
                     (else
                      (list operation (simplify a) (simplify b)))))
          ((^) (cond ((and (number? a) (number? b))
                      (cond ((and (zero? a) (zero? b)) (error "A and B are both 0, statement undefined!"))
                            ((zero? a) (if (< b 0)
                                           (error "Exponent undefined for 0 and negative B.")
                                           0))
                            ((zero? b) 1)
                            ((= a 1) 1)
                            ((= b 1) a)
                            (else (expt a b))))
                     ((number? a)
                      (cond ((zero? a) 0) ;; depends on b actually - if b < 0 then undefined
                            ((= a 1) 1)
                            (else (list operation (simplify a) (simplify b)))))
                     ((number? b)
                      (cond ((zero? b) 1) ;; depends on a actually - if a = 0 then undefined
                            ((= b 1) (simplify a))
                            (else (list operation (simplify a) (simplify b)))))
                     (else (list operation (simplify a) (simplify b)))))))))

旧版本

(define (simplify expr)
  (if (not (list? expr))
      expr
      (let ((operation (car expr))
            (a (simplify (cadr expr)))
            (b (simplify (caddr expr))))
        (case operation
          ((+) (cond ((and (number? a) (= a 0)) b)
                     ((and (number? b) (= b 0)) a)
                     ((and (number? b) (number? a)) (+ a b))
                     (else (list operation (simplify a) (simplify b)))))
          ((-) (cond ((and (number? a) (= a 0)) (- b))
                     ((and (number? b) (= b 0)) a)     
                     ((and (number? a) (number? b)) (- a b))
                     (else (list operation (simplify a) (simplify b)))))
          ((*) (cond ((and (number? a) (= a 1)) b)
                     ((and (number? a) (= a 0)) 0)
                     ((and (number? a) (number? b) (= b 1)) a)
                     ((and (number? a) (number? b) (= b 0)) 0)
                     ((and (number? a) (number? b)) (* a b))
                     ((and (number? b) (= b 1)) a)               ;; added by me
                     ((and (number? b) (= b 0)) 0)
                     (else (list operation (simplify a) (simplify b)))))
          ((/) (cond ((and (number? a) (= a 0)) 0)
                     ((and (number? a) (number? b) (= b 1)) a)
                     ((and (number? a) (number? b) (= b 0)) (error "Divison by 0, statement undefined!")) ;; error added
                     ((and (number? a) (number? b)) (/ a b))
                     ((and (number? b) (= b 1)) a)               ;; added by me
                     ((and (number? b) (= b 0)) (error "Divison by 0, statement undefined!")) ;; error added
                     (else (list operation (simplify a) (simplify b)))))
          ((^) (cond ((and (number? a) (= a 1)) 1)
                     ((and (number? a) (number? b) (= a 0) (= b 0)) (error "A and B are both 0, statement undefined!"))
                     ((and (number? a) (number? b) (= b 0)) 1)
                     ((and (number? a) (number? b) (= b 1)) a)
                     ((and (number? a) (number? b)) (expt a b))
                     ((and (number? b) (= b 1)) a)               ;; added by me
                     ((and (number? b) (= b 0)) 1)               ;; corrected to 1 (before: zero)
                     (else (list operation (simplify a) (simplify b)))))))))

这可以正确简化. 我以一种非常低效的方式(随后是cond)编写了它-我将简化"答案:D.我在评论中添加了几行. 它简化了(simplify '(+ (* (^ x 5) 0) (* 3 (* 5 (* (^ x 4) 1)))))'(* 3 (* 5 (^ x 4)))更好(技巧是递归地在else子句中的每个操作数上调用simplify. 但是,我想在结尾加上(* 15 (^ x 4)).为此,我们需要更多检查...

This is my function for differentiation that works correctly.

#lang racket

(define (diff x expr) 
  (if (not (list? expr))
      (if (equal? x expr) 1 0) 
  (let ( (operation (car expr)) 
         (u (cadr expr))
         (v (caddr expr)))
       (case operation
          ((+) (list '+ (diff x u) (diff x v))) 
          ((-) (list '- (diff x u) (diff x v))) 
          ((*) (list '+                             
                     (list '* u (diff x v))
                     (list '* v (diff x u))))       
          ((/) (list '/ (list '- (list '* v (diff x u)) (list '* u (diff x v))) 
                     (list '* v v)))                
          ((^) (list '* v (list '* (list '^ u (- v 1)) (diff x u ))))                           
))))

and now I also have most of the simplification function working correctly but there is a problem somewhere and i think it is in my power rule simplifier

(define(simplify expr)
  (if (not (list? expr)) expr
  (let ((operation (car expr))      
       (a (simplify (cadr expr)))   
       (b (simplify (caddr expr)))) 
   (case operation 
        ((+) (if (and (number? a)(= a 0)) b    
                 (if (number? b) (if (= b 0) a 
                                     (+ a b)) 
                 (list operation a b)))) 

        ((-) (if (and (number? a) (= a 0)) (- b)         
                 (if (number? b) (if (= b 0) a  
                                     (- a b))
                 (list operation a b)))) 

        ((*) (cond [(number? a)
                    (cond [(= 1 a) b]
                          [(= 0 a) 0]
                          [else (if (number? b)
                                    (cond [(= b 1) a]
                                          [(= b 0) 0]
                                          [else (* a b)])
                                    (list operation a b))])]
                   [(and (number? b) (= b 0)) 0]
                   [(list operation a b)]))
;The case a/b where b=1 is currently only simplified if a is number. Insert an extra case into the cond expression handling b=1
        ((/) (cond [(number? a)
                    (cond [(= 1 b) a]
                          [(= 0 a) 0]
                          [else (if (number? b)
                                    (cond [(= b 1) a]
                                          [(= b 0) 0]
                                          [else (/ a b)])
                                    (list operation a b))])]
                   [(and (number? b) (= b 0)) 0]; this is where an error should be thrown
                         (cond [(= b 1) 1]
                   [(list operation a b)]))

       ((^) (cond [(number? a)
                   ;if a is 1, 1^x is always 1
                   (cond [(= a 1) 1]
                         [else (if (number? b)
                                   ;if a and b are 0 throw error else anything ^0 is 1.
                                   (cond [(= b 0) (if (= a 0) (error "A and B are both 0, statement undefined!") 1)]
                                         ;if b is 1, x^1 is always x
                                         [(= b 1) a]
                                         ;else a^b
                                         [(expt a b)])
                                   ;a or b are continuations
                                   (list operation a b))])]                                  
                   [else (list operation a b)]))
 ))))

I have run many tests and most pass but there are a few that don't and I can't figure out why.

(simplify '(/ x 1)) ;why is this not working correctly
(simplify '(+ (* (^ x 5) 0) (* 3 (* 5 (* (^ x 4) 1)))) ;not simplifying correctly
(simplify '(* 3 (* (^ x 2) 1))) ;not simplifying correctly
;(simplify '(/ 1 0));not working 
;(simplify '(^ 0 0));this works fine just returns an exception

解决方案

Shorter Solution

By using Racket's intrinsic ability to calculate number (and do the correct error messaging e.g. division by zero error)

#lang racket

(define (simplify expr)
  (if (not (list? expr)) 
      expr
      (cond ((= 1 (length expr)) (simplify (car expr)))
            ((= 2 (length expr))
             (let ((operation (car expr))
                   (a (simplify (cadr expr))))
               (case operation
                 ((-) (cond ((number? a) (- a))
                            (else (list operation (simplify a)))))
                 ((+) (cond ((number? a) a)
                            (else (simplify a))))
                 (else (error "Diadic operator with only one argument given.")))))
            ((= 3 (length expr))        
             (let ((operation (car expr))        
                   (a (simplify (cadr expr)))     
                   (b (simplify (caddr expr))))   
               (case operation  
                 ((+) (cond ((and (number? a) (number? b)) (+ a b)) 
                            ((number? a) 
                             (cond ((zero? a) (simplify b)) 
                                   (else (list operation (simplify a) (simplify b))))) 
                            ((number? b) 
                             (cond ((zero? b) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))
                 ((-) (cond ((and (number? a) (number? b)) (- a b)) ;; use Racket's ability to subtract
                            ((number? a)
                             (cond ((zero? a) (- (simplify b)))
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))
                 ((*) (cond ((and (number? a) (number? b)) (* a b)) ;; use Racket's ability to mulitpy
                            ((number? a)
                             (cond ((zero? a) 0)
                                   ((= a 1) (simplify b))
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) 0)
                                   ((= b 1) (simplify a))
                                   (else (list operation (simplify a)(simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))
                 ((/) (cond ((and (number? a) (number? b)) (/ a b)) ;; use Racket's ability to divide
                            ((number? a)
                             (cond ((zero? a) 0)
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) (error "Divison by 0, statement undefined!"))
                                   ((= b 1) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else
                             (list operation (simplify a) (simplify b)))))
                 ((^) (cond ((and (number? a) (number? b)) (expt a b)) ;; use Racket's ability to exponentiate
                            ((number? a)
                             (cond ((= a 1) 1)  ;; ((zero? a) 0) ;; depends on b [b < 0 then undefined]
                                   (else (list operation (simplify a) (simplify b)))))
                            ((number? b)
                             (cond ((zero? b) 1) ;; depends on a [a = 0 then undefined]
                                   ((= b 1) (simplify a))
                                   (else (list operation (simplify a) (simplify b)))))
                            (else (list operation (simplify a) (simplify b)))))))))))

For some examples, which end up in numbers and operators only, it helps to apply simplify several times. E.g. (simplify (simplify (simplify '(+ (+ (* 2 1) (* x 0)) 0)))) returns 2.

Explanation of the shorter solution

At the beginning, the if-clause tests, whether the expression is a list or not (not (list? expr)). If not, it means the expression is either a number or a variable symbol. So we return them (expr). Otherwise, we know that expr is composed - a list - of the form (<operator - one of */-+^> <first-operand - called a here> <second-operand 'b'>). We test for the length of the lists by the cond clauses. If the length is 1 (= 1 (length expr)), it means that some superfluous parentheses were set, like ((+ 1 2)) or (1), so we return (car expr) to let the expression get rid of superfluous parantheses. If the length of the list is 2, then we can handle cases like (+ 3) => 3 or such kind of superfluous plus sign. or more complex expressions (+ (* 3 4)). So in all cases we recursively call simplify on the results.

If the expression has the length 3, we parse the operation, the first operand a and the second operand b. Then according to the operator, we have different simplification rules to follow. However the instructions share all a similar pattern.

;; each of the case branches has the pattern:
((<operator>) (cond ((and (number? a) (number? b)) (<operator> a b))
                    ((number? a)
                    ...)
                    ((number? b)
                    ...)
                    (else
                      (list operation (simplify a) (simplify b)))))

The first clause with the condition (and (number? a) (number? b)) covers the case that both operands are numbers. This case is simple to handle, because Racket "knows" already how to handle the basic mathematical operations. So you just calculate the value and return it as the simplification (thus: (<operator> a b) as return value). Note: In case of /, if b is zero, then a Division by zero error has to be raised. But Racket raises it automatically if we give zero for b, so here we just let Racket do also the error raising and just let return and calculate Racket (/ a b).

The second clause is the case that a is a number. Since the first test must have failed, we now know for sure that at least one of a or b if not both are composed forms or symbols and not numbers. If this condition is true, we know that a is a number, but b must be composed. (If b would be a number, the first condition would have given true ...). As instruction, we have again a cond with clauses. The clauses handle the special cases of a as a number. Addition + and Subtraction - both are invariant to 0, thus the first check for both branches here is, whether a is zero (zero? a). In this case, we leave out operator and a=0 and return only b. But since b is for sure a composed form, we call simplify on it, to recursively simplify b (otherwise, the expression b would be given as it is without further simplification). If a is not zero, we arrive to the else clause, knowing that a is a non-zero number and b must be a composed form. Thus, we list the operator and a and b each of them simpified. Actually a doesn't need to be simplified further, since we know, it is a number. You could remove the simplify call around a and just write a. For * and / we distinguish 3 cases:

  • if a is zero for * or /, then we know that everything becomes zero, so we return at this point 0 for the entire expression.
  • then, we ask for the invariant for * or /, which is 1. Thus (= 1 a) So we return in this case (simplify b) without a and the operator.

For the third clause, the case that b is a number and a composed, everything is the same like in the previous clause - just replacing everywhere b for a. And only one exception: for / if b is zero, then a division by zero error has to be given. Thus we raise here an error by (error "Divison by 0, statement undefined!"). Since a is not a number, we cannot let Racket calculate here out anything, so we raise manually an error.

The fourth clause (else) will be invoked only if a and b, are themselves composed, since all previous tests in this path failed. In this case, we recursively call (simplify a) and (simplify b) and list those results together with the operator as a list. So for each operand the here described simplification process will be applied.

Important: The let-bindings for a and b call also simplify on the operands. If we leave out the simplify calls here, the simplification stops after one level. So both simplify calls - those in the let bindings - and those at the end of the cond-branches downstream - are necessary to "pull" the recursion down through the entire expression tree - as we have seen day before yesterday, when I forgot the simplify in the cond clauses ;) . So these two layers of simplify calls act like motors to pull the simplification process all the way down to the bottom ...

Solution

(define (simplify expr)
  (if (not (list? expr))
      expr
      (let ((operation (car expr))
            (a (cadr expr))
            (b (caddr expr)))
        (case operation
          ((+) (cond ((and (number? a) (number? b))
                      (cond ((zero? a) b)
                            ((zero? b) a)
                            (else (+ a b))))
                     (else (list operation (simplify a) (simplify b)))))
          ((-) (cond ((and (number? a) (number? b))
                      (cond ((zero? a) (- b))
                            ((zero? b) a)
                            (else (- a b))))
                     (else (list operation (simplify a) (simplify b)))))
          ((*) (cond ((and (number? a) (number? b))
                      (cond ((or (zero? a) (zero? b)) 0)
                            ((= a 1) b)
                            ((= b 1) a)
                            (else (* a b))))
                     ((number? a)
                      (cond ((zero? a) 0)
                            ((= a 1) (simplify b))
                            (else (list operation (simplify a) (simplify b)))))
                     ((number? b)
                      (cond ((zero? b) 0)
                            ((= b 1) (simplify a))
                            (else (list operation (simplify a)(simplify b)))))
                     (else (list operation (simplify a) (simplify b)))))
          ((/) (cond ((and (number? a) (number? b))
                      (cond ((zero? b) (error "Divison by 0, statement undefined!"))
                            ((zero? a) 0)
                            ((= b 1) a)
                            (else (/ a b))))
                     ((number? a)
                      (cond ((zero? a) 0)
                            (else (list operation (simplify a) (simplify b)))))
                     ((number? b)
                      (cond ((zero? b) (error "Divison by 0, statement undefined!"))
                            ((= b 1) (simplify a))
                            (else (list operation (simplify a) (simplify b)))))
                     (else
                      (list operation (simplify a) (simplify b)))))
          ((^) (cond ((and (number? a) (number? b))
                      (cond ((and (zero? a) (zero? b)) (error "A and B are both 0, statement undefined!"))
                            ((zero? a) (if (< b 0)
                                           (error "Exponent undefined for 0 and negative B.")
                                           0))
                            ((zero? b) 1)
                            ((= a 1) 1)
                            ((= b 1) a)
                            (else (expt a b))))
                     ((number? a)
                      (cond ((zero? a) 0) ;; depends on b actually - if b < 0 then undefined
                            ((= a 1) 1)
                            (else (list operation (simplify a) (simplify b)))))
                     ((number? b)
                      (cond ((zero? b) 1) ;; depends on a actually - if a = 0 then undefined
                            ((= b 1) (simplify a))
                            (else (list operation (simplify a) (simplify b)))))
                     (else (list operation (simplify a) (simplify b)))))))))

Old version

(define (simplify expr)
  (if (not (list? expr))
      expr
      (let ((operation (car expr))
            (a (simplify (cadr expr)))
            (b (simplify (caddr expr))))
        (case operation
          ((+) (cond ((and (number? a) (= a 0)) b)
                     ((and (number? b) (= b 0)) a)
                     ((and (number? b) (number? a)) (+ a b))
                     (else (list operation (simplify a) (simplify b)))))
          ((-) (cond ((and (number? a) (= a 0)) (- b))
                     ((and (number? b) (= b 0)) a)     
                     ((and (number? a) (number? b)) (- a b))
                     (else (list operation (simplify a) (simplify b)))))
          ((*) (cond ((and (number? a) (= a 1)) b)
                     ((and (number? a) (= a 0)) 0)
                     ((and (number? a) (number? b) (= b 1)) a)
                     ((and (number? a) (number? b) (= b 0)) 0)
                     ((and (number? a) (number? b)) (* a b))
                     ((and (number? b) (= b 1)) a)               ;; added by me
                     ((and (number? b) (= b 0)) 0)
                     (else (list operation (simplify a) (simplify b)))))
          ((/) (cond ((and (number? a) (= a 0)) 0)
                     ((and (number? a) (number? b) (= b 1)) a)
                     ((and (number? a) (number? b) (= b 0)) (error "Divison by 0, statement undefined!")) ;; error added
                     ((and (number? a) (number? b)) (/ a b))
                     ((and (number? b) (= b 1)) a)               ;; added by me
                     ((and (number? b) (= b 0)) (error "Divison by 0, statement undefined!")) ;; error added
                     (else (list operation (simplify a) (simplify b)))))
          ((^) (cond ((and (number? a) (= a 1)) 1)
                     ((and (number? a) (number? b) (= a 0) (= b 0)) (error "A and B are both 0, statement undefined!"))
                     ((and (number? a) (number? b) (= b 0)) 1)
                     ((and (number? a) (number? b) (= b 1)) a)
                     ((and (number? a) (number? b)) (expt a b))
                     ((and (number? b) (= b 1)) a)               ;; added by me
                     ((and (number? b) (= b 0)) 1)               ;; corrected to 1 (before: zero)
                     (else (list operation (simplify a) (simplify b)))))))))

This simplifies correctly. I wrote it in a quite inefficient way (with consequent cond) so - I will "simplify" the answer :D . I put comment which lines I added. It simplifies (simplify '(+ (* (^ x 5) 0) (* 3 (* 5 (* (^ x 4) 1))))) to '(* 3 (* 5 (^ x 4))) which is better (the trick is to recursively to call simplify on each of the operands in the else clauses. However, I'd like to have (* 15 (^ x 4)) at the end. For this, we need some more checks ...

这篇关于如何为球拍中有效的符号微分函数修复幂函数的简化函数(使结果更易于阅读)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆