1

I'm trying to write a Scheme function that takes two parameters, a list of numbers and another number, and returns true if a subset of the list adds up to that number.

Example Output:

> (groupSum '(1 2 5) 7)
> #t
> (groupSum '(1 2 5) 4)
> f

So far, all I am able to do is get the sum of all the numbers in a list. What do I need to do with my code to reach the example output?

Here's my code so far:

(define (groupSum elemList)
  (if
    (null? elemList)
    0
    (+ (car elemList) (groupSum (cdr elemList)))
  ))

What my code returns:

> (groupSum '(1 2 5))
> 8
Will Ness
  • 62,652
  • 8
  • 86
  • 167
Bryce S
  • 39
  • 1
  • 8

1 Answers1

0

So what you ask is, does a subset of a given list exist (in a positional sense, i.e. keeping duplicates, if any) that adds up to a given number.

To go through subsets is the job of the powerset function, except we want to jump out of it and return #t as soon as we detect success. Otherwise, just return #f in the end.

Augmenting a powerset function from one of my other answers on this site, we get

(define (group-adds-to aL aN)
  (call/cc
   (lambda (......)  ;; 2.
     (letrec 
        ([...... (lambda (aL)  ;; 4.
           (cond
             [(empty? aL) (list empty)]
             [else
              (add-into   ;; 5b.
                        (powerset (rest aL))  ;; 3b.
                        (first aL))]))]

         [...... (lambda (r a)  ;; 6.       ; `r` is recursive result, `a` an element
           (cond
             [(empty? r) empty]             ; nothing to add `a` to
             [else
              (cons (add a (first r)) ;; 7. ; either add `a`,
                    (cons (first r)         ;   or not, to the first in `r`
                          (add-into  ;; 5a. ; and recursively proceed
                           (rest r)         ;   to add `a` into the rest of `r`
                           a )))]))]

         [......                      ;; 8.
                  (lambda (......)       ;; 9...
                     (let ([sum ......])   ;; 10...
                       (if (= sum aN)  ;; 11.
                           (return #t)  ;; 1.
                           sum)))])

       (powerset aL)  ;; 3a.
       #f))))   ;; 12.

Fill in the blanks!

Testing:

> (group-adds-to '(1 2 5) 7)
#t
> (group-adds-to '(1 2 5) 4)
#f

It takes a few trivial changes to make this #r5rs compliant, if you need it to be.

Of course this code can be streamlined and shortened with the use of more built-in functions. It is quite simple to get rid of call/cc here, too. You can also get fancy and add checks to shorten the interim lists, detecting and removing such subgroups (subsets) that can't possibly contribute to a successful outcome.


OK, how to fill in the blanks.

  1. return? what is that?
  2. it is an escape continuation set up for us by call/cc. When (if) it is called, as ;; 1., the value #t is returned right away as the final result of the whole group-adds-to function call, no matter how deep inside the recursion we are at that point. The recursion is just abandoned, and the result is just returned.
    And if return was never called, because the equality (= sum aN) ;; 11. was never detected, the recursion will end its course and #f will be returned normally, as the last expression in a function's body, at ;; 12..
  3. a,b. powerset? what is that?
  4. it is an internal function we've defined
  5. a,b. add-into?
  6. it too, defined here.
  7. and add?
  8. yep.
  9. Do it yourself. See how it is used, at ;; 7, what its arguments are, what must it return.
  10. Do It Yourself. You can do it!
Will Ness
  • 62,652
  • 8
  • 86
  • 167
  • Thanks for your help! I'm new to Scheme so I still don't understand it very much. What should I be doing in the blanks? – Bryce S Apr 22 '18 at 19:11
  • read the code closely and put there what's missing. :) If you knew Scheme, it'd be easy enough. If you don't, SO is not the place where it could be taught to you, sorry. if you have a specific question, ask away. :) – Will Ness Apr 22 '18 at 19:16
  • but first, just so I get my bearings, do you understand what this "powerset" stuff has got to do with your problem? – Will Ness Apr 22 '18 at 19:20
  • Yeah “powerset” allows you to take subsets of a given set? Which is what I’m trying to do for this problem to check all the possible sums of numbers within the set. – Bryce S Apr 22 '18 at 19:48
  • exactly. and instead of keeping the subsets-so-far we keep their sums... – Will Ness Apr 22 '18 at 19:52
  • have you succeeded in filling in the blanks? you can post your code on some site like codepad.org if you want me to take a look, or you can post a new question with it here on stackoverflow (if you have more questions about it, that is). a good followup is to try implementing what is suggested in the last paragraph, about skipping some subgroups early, to keep their numbers in check. otherwise it can be quite heavy with the n! total subgroups, potentially. – Will Ness Apr 23 '18 at 06:00
  • Yes! I was able to get it to work, thanks for your help! – Bryce S Apr 24 '18 at 16:02
  • great! were you able to get rid of call/cc, and reduce the number of interim lists? it takes only a small tweak of `add-into`, for both. – Will Ness Apr 24 '18 at 16:16
  • No, I wasn't able to. I haven't had the time to further optimize the function once I got it working. – Bryce S Apr 25 '18 at 20:01
  • if you ever have time, here's a hint: you replace `(return #t)` in `add` to just `#t`, and change `add-into` to `(lambda (r a) (cond [(eq? #t r) #t] ...))` and make further changes so everything fits together. Thus instead of consing, when first #t is achieved it is just returned straight up the chain of calls. It's the "regular" way to achieve this effect, while you still learning. if this is for a test, any use of call/cc is a strong signal that you haven't written the code yourself. call/cc is considered an advanced feature. – Will Ness Apr 26 '18 at 07:01