schemefoldpowerset

Implementing powerset in scheme


I am trying to implement a powerset function in Scheme in two ways. One way is using tail recursion, and I did it like this:

(define (powerset list)
 (if (null? list) '(())                                    ;; if list is empty, its powerset is a list containing the empty list
  (let ((rest (powerset (cdr list))))                   ;; define "rest" as the result of the recursion over the rest of list
    (append (map (lambda (x) (cons (car list) x)) rest) ;; add the first element of list to the every element of rest (which is a sublist of rest) 
            rest))))                                    ;; and append it to rest itself (as we can either use the current element (car list), or not

Which works fine.

Another way is using foldr, and this is where I face some issues. My current implementation is as follows:

(define (powerset-fr list)
 (foldr (lambda (element result)        ;; This procedure gets an element (and a result);
       (if (null? result) ;; if starting with the empty list, there is nothing to "fold over".
           (cons '() (cons element result))
           (foldr (lambda (inner-element inner-result)
                    (append (cons element result) inner-result))
                  '(())
                  result)))
     '()                             ;; The result is initialized to the empty list,
     list))                         ;; and the procedure is being applied for every element in the first list (list1)

Which yields a poor result.

I'll try to explain shortly how did I approach this problem so far:

foldr runs over every element in the given set. For each such element, I should add some new elements to the powerset. Which elements should these be? One new element for each existing element in the powerset, where is append the current element in list to the existing element in powerset.

This is why I thought I should use foldr twice in a nested way - one to go over all items in given list, and for each item I use foldr to go over all items in "result" (current powerset).

I faced the problem of the empty list (nothing is being added to the powerset), and thus added the "if" section (and not just foldr), but it doesn't work very well either.

I think that's it. I feel close but it is still very challenging, so every help will be welcomed. Thanks!


Solution

  • The solution is simpler, there's no need to use a double foldr, try this:

    (define (powerset-fr lst)
      (foldr (lambda (e acc)
               (append (map (lambda (x) (cons e x)) 
                            acc) 
                       acc))
             '(())
             lst))
    

    If your interpreter defines append-map or something equivalent, then the solution is a bit shorter - the results will be in a different order, but it doesn't matter:

    (define (powerset-fr lst)
      (foldr (lambda (e acc)
               (append-map (lambda (x) (list x (cons e x)))
                           acc))
             '(())
             lst))
    

    Either way, it works as expected:

    (powerset-fr '(1 2 3))
    => '((1 2 3) (1 2) (1 3) (1) (2 3) (2) (3) ())