3
votes

I'm making a function which adds big numbers together manually (no library function) and I'm having some trouble capturing the result to display. I simply display back '(). Here's a little back ground of how it should work: If I pass (big-add1 '(999) '(456) 0), I should return '(455 1). If I pass (big-add1 '(999 234 681) '(456) 0) I should return '(455 235 681). But I've had no success displaying anything other than the empty list so far. Here's my code right now:

(define (big-add1 x y co)
(cond
;; If both lists are empty, the return value is either 0 or the carryover value.
[(and (= 0 (length x)) (= 0 (length y)))
  (if (= co 0) '() (list co))]
[(= 0 (length x))  (big-add1 (list co) y 0)]
[(= 0 (length y))  (big-add1 x (list co) 0)]
[else
 (cond(< (length x) (length y)) (big-add1 y x 0)) ;reverse the order of parameters
 (append (list(+ (modulo (car x) 10) (modulo (car x) 10) co))) ;trying to construct a result
 (if(>(+ (modulo (car x) 10) (modulo (car x) 10) co) 9)
    (letrec ([co 1]) co) ;if addition produces a double digit number set carryover value
    (letrec ([co 0]) co));if addition produces a single digit number
 (if(or(> (car x) 10) (> (car y) 10)) ;we got down to single digits
    (big-add1(append(list(quotient (car x) 10)) (cdr x)) (append(list(quotient (car y) 10)) (cdr y)) co)
    (big-add1 (cdr x) (cdr y) co))
 ]
))

(big-add1 '(999) '(456) 0)
(big-add1 '(999 234 681) '(456) 0)

Bonus question: If anyone is feeling up for it, I can see in debug mode that co is not getting changed to 1 when the sum is greater than 10. It seems to execute the line, but not actually change it. Could anyone clarify what is going on? I super new to this, so if anyone has any suggestions on how to simplify it, please feel free to let me know. I would really appreciate it. Thank you for your time.

3
Indent please. Use the tab key in DrRacket. append doesn't mutate. Use let to bind names to intermediate results. Then combine those.Dan D.
@DanD. I indented the code a little more. If there's anything else that I need to indent, please let me know. Could you please elaborate or provide an example of what you mean by "Use let to bind names to intermediate results. Then combine those."?aurora91
Rather than changing the value of a name, simply shadow it. (let ((x 1)) (let ((x (+ x 1))) x)) evaluates to 2. The second let shadows the binding of the first.Dan D.
Your entire function is written in such a way that it looks like you think that you're doing a sequence of mutations, like you would in Java. You're not - you're computing values that you throw away. I recommend that you review the earlier chapters, and exercises, in your book before proceeding with this.molbdnilo
@aurora91 No textbook? That's really unfair. Luckily, one of the best programming books ever written is in Scheme and available for free here. (And there are lectures about it on youtube.)molbdnilo

3 Answers

3
votes

Firstly, there are bunch of mistakes in your code. I'm listing couple of big ones:

  • Scheme does not return unless it's the last expression. The first else clause's cond expression is the typical one not to return.
  • append does not change the given list's content. It is the ;; trying to... comment part. Not sure what you wanted to do.
  • The use of letrec does nothing. If you want to change the bound value, then use set!.

Secondly, the followings are not mistakes but just tips:

  • You don't have to make a list then append to create a list. Simply use list. Using cons to construct a list then reverse the result is one of the idiom commonly used to return a list. So if you see using append, then consider this.
  • There's null? procedure to check empty list. If you want to check it using this instead of comparing its length with 0.

Finally, following is the one that works as your requirement.

;; each element must be less than 1000
(define (big-add1 x y co)
  (let loop ((x x) (y y) (co co) (r '()))
    (cond
     ;; If both lists are empty, the return value is either 0
     ;; or the carryover value.
     [(and (null? x) (null? y))
      ;; if there's carry then we need to add it
      (if (zero? co) (reverse r) (reverse (cons co r)))]
     [(null? x) (loop x (cdr y) 0 (cons (+ co (car y)) r))]
     [(null? y) (loop (cdr x) y 0 (cons (+ co (car x)) r))]
     [else
      (let ((r (+ (car x) (car y) co))) ;; add elements
        ;; separate result into element+carry
        ;; NB: it's better not to put magic number.
        (let-values (((e co) (if (> r 1000)
                                 (values (modulo r 1000) 1)
                                 (values r 0))))
          ;; next
          (loop (cdr x) (cdr y) co (cons e r))))])))
2
votes

You look like you're in the same class as I am, so I might be able to help out a bit. Takashi Kato above has already answered your question about how racket/scheme returns results (only the last expression will return a result) but I think I can elaborate a bit more on the solution you're trying to get to.

As you've described in your question, the big-add function takes two arguments that are basically a list of "chunks" that represent a number e.g. the list '(999 456) is handled as a number analogous to 456,999. Each "chunk" within a given list can only have a maximum range from 0 to 999 (assuming we're just adding positive numbers here).

Although Mr. Kato's solution may work, Dr. Austin (if we have the same prof) will prefer that you to complete this problem in a recursive way and return an answer in the same format as the arguments passed in (a list of "chunks"). The lists provided need not be reversed to work.

Here's a way to think about it:

  • When you're adding "chunks" of numbers together, you append or cons the sums of each chunk together to create a list for returning your result.
  • If a chunk's sum exceeds 999, that means it will have a carryover value. As the maximum value of a single chunk is 999, a value of 1 in the next chunk is equivalent to 1000 in the previous chunk -- meaning you must subtract (or modulo, whatever your preference is as they accomplish the same task) 1000 within your current chunk and then pass a value of 1 over to the next calculation.
  • Because of this, each chunk sum will be the sum of the x chunk, the y chunk, and the carryover value (if there is one) as we move from each chunk in each list to the next.

The algorithm looks kind of like this:

  1. Calculate the chunk sum by adding car x, car y, and co together: (+ (car x) (car y) co). You can just store this value into an ID by using let. I called mine chunk-sum but you can call it whatever makes it easier for you to understand. Within the body of our let, you'll have to define what to do with our chunk-sum and how to start creating the list that you return.
  2. If chunk-sum is equal to or greater than 1000 or MAX_BLOCK_VALUE (they are equivalent if you've defined that ID), then there will be a carryover value, so you will want to use a cond or if.
  3. Create a list using append or cons. I find cons more preferable for this solution since cons can take a number element as its first argument, where append only takes lists (thus requiring you to use list on chunk-sum). In this step you will have to recursively call the function big-add1 to continually append elements to your list. Your code for the recursive call will look similar to (big-add1 (cdr x) (cdr y) 0) if not exactly.
  4. The function will return a value after we reach our base case. In this case, our base case is when there are no more chunks to add together.

So here's the code you'll try to complete:

#lang racket

(define MAX_BLOCK_VALUE 1000)

;; Addition of two big-nums
(define (big-add x y)
  (big-add1 x y 0)
  )

(define (big-add1 x y co)
  (cond
    ;; If both lists are empty, the return value is either 0 or the carryover value. This is our base case
    [(and (= 0 (length x)) (= 0 (length y)))
     (if (= co 0) '() (list co))]
    [(= 0 (length x))  (big-add1 (list co) y 0)]
    [(= 0 (length y))  (big-add1 x (list co) 0)]
    [else
     #| code is here |#
     (let ((chunk-sum (+ (car x) (car y) co)))
       (if (>= chunk-sum MAX_BLOCK_VALUE)
           ;; use cons/append and call recursive step for if we have carryover
           (error "foo")
           ;; use cons/append and call recursive step for if we don't have carryover
           (error "bar")
           )
       )
     ]
    ))

Hope this helps.

0
votes

I was able to figure it out by simplifying my algorithm by a lot. Here's what I ended up with:

(define (big-add1 x y co)
(cond
;; If both lists are empty, the return value is either 0 or the carryover value.
[(and (= 0 (length x)) (= 0 (length y)))
  (if (= co 0) '() (list co))]
[(= 0 (length x))  (big-add1 (list co) y 0)]
[(= 0 (length y))  (big-add1 x (list co) 0)]
[else
 (cons(modulo (+ (car x) (car y) co) 1000)
 (if(>(+ (car x) (car y) co) 1000)
    (big-add1 (cdr x) (cdr y) 1)
    (big-add1 (cdr x) (cdr y) 0))
    )

 ]
))