1

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
Royale_w_cheese
  • 297
  • 2
  • 9
  • 3
    What are the expected result value of the expressions you have marked as not working correctly? – soegaard Oct 18 '18 at 19:54
  • '(/ x 1)) should simplify to x, (+ (* (^ x 5) 0) (* 3 (* 5 (* (^ x 4) 1)))) should simplify to 15x^4, and '(* 3 (* (^ x 2) 1)) should simplify to 3x^2 – Royale_w_cheese Oct 18 '18 at 21:41
  • 1
    In the case `/`, `(number? b)`, I don't think that cond-answer is correct. When `b` is equal to `0` the answer isn't `0`, it's undefined. – Alex Knauth Oct 18 '18 at 21:44
  • that is a good catch Alex Knauth. I should throw an error, but I don't think that is at the root of the problem . And am new to racket so although I just tried to throw an error, my syntax must be wrong because I continue to get 0 when dividing by 0 instead of an error showing undefined – Royale_w_cheese Oct 18 '18 at 22:27
  • 1
    What does the marked expressions return currently? – soegaard Oct 19 '18 at 13:31
  • (simplify '(/ x 1)) returns '(/ x 1) – Royale_w_cheese Oct 19 '18 at 15:52
  • 1
    You have in a comment: `; /` `; b is a number and b=1 -> a`, but I don’t see a case in your code that corresponds to that. I do see something for `; /` `; b is a number and b=0`, so maybe try adding something next to that. – Alex Knauth Oct 19 '18 at 15:54
  • Alex Knauth good catch but honestly I've been staring at this code for so long, I'm not sure where to start? – Royale_w_cheese Oct 19 '18 at 16:26
  • 2
    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. – soegaard Oct 19 '18 at 19:13

1 Answers1

1

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 ...

Gwang-Jin Kim
  • 9,303
  • 17
  • 30
  • I've got to take a short break from staring at this screen for hours on end and take my dog for a walk. Then I will try this new implementation. Thank you so much for all this effort. Just by glancing at it, it looks like it will work. – Royale_w_cheese Oct 19 '18 at 21:52
  • 1
    @Royale_w_cheese Welcome! Take your break! I am also staring too long at the screen! Thanks also for inspiring question! :) I would not have so easily tried to jot this down - a simpifier and a differentiator. – Gwang-Jin Kim Oct 19 '18 at 21:55
  • Testing the shorter solution and the solution, I get some expressions to simplify that weren't simplifying before, but many that were simplifying before are no longer simplifying now. – Royale_w_cheese Oct 19 '18 at 22:08
  • For example, (simplify '(* (^ x 5) 3)) is not working. (simplify '(* 3 (* (^ x 2) 1))) is also not working just to name a couple. and that was actually using what is labeled the old version. The other two solutions, don't simplify most of my test expressions. – Royale_w_cheese Oct 19 '18 at 22:12
  • okay, I will have a look. Hm it works ... I mean `(* (^ x 5) 3)` cannot be further simplified. And `(simplify '(* 3 (* (^ x 2) 1)))` returns `'(* 3 (^ x 2))` - maybe you have to open a new window and try it there? – Gwang-Jin Kim Oct 19 '18 at 22:14
  • i think the two recursive calls I make towards the beginning of my simplify code is actually the best way to go. It's just figuring out the one or two errors that I am overlooking is the problem. – Royale_w_cheese Oct 19 '18 at 22:18
  • Okay ... I was thinking too, whether the recursive call at the beginning saves a lot of typing ... In my oldest version I marked the few cases you overlooked ... – Gwang-Jin Kim Oct 19 '18 at 22:19
  • the point of my simplify function is to make the results of my differentiator easy to read so this expression (* (^ x 5) 3), should simplify to 3x^5 and '(* 3 (^ x 2)) should simplify to 3x^2 when I get this working correctly – Royale_w_cheese Oct 19 '18 at 22:19
  • 1
    ah okay - that I couldn't see in the long question part :) . Okay, I could try a little ... – Gwang-Jin Kim Oct 19 '18 at 22:21
  • But you know, this simplification requires much more code - because you have to look one simplification step in advance ... – Gwang-Jin Kim Oct 19 '18 at 22:22
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/182173/discussion-between-gwang-jin-kim-and-royale-w-cheese). – Gwang-Jin Kim Oct 19 '18 at 22:23
  • Can someone comment out this simplifier (shorter solution) code above in a very detailed manner so I can understand exactly what is going on in each line please. I have an overall idea but need and want to understand completely. Thanks so much. – Royale_w_cheese Oct 22 '18 at 18:51
  • I'm not sure how to keep in touch with you @Gwang-Jin Kim. If i send a message through the chat link above, are you alerted or is there a better way to communicate? – Royale_w_cheese Oct 24 '18 at 18:11
  • @Royale_w_cheese: then look there again :) – Gwang-Jin Kim Oct 27 '18 at 20:44