Scheme 在方案中实现powerset

Scheme 在方案中实现powerset,scheme,fold,powerset,Scheme,Fold,Powerset,我试图用两种方法在Scheme中实现powerset函数。 一种方法是使用尾部递归,我是这样做的: (define (powerset list) (if (null? list) '(()) ;; if list is empty, its powerset is a list containing the empty list (let ((rest (powerset (cdr list))))

我试图用两种方法在Scheme中实现powerset函数。 一种方法是使用尾部递归,我是这样做的:

(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
这很好用

另一种方法是使用foldr,这就是我面临的一些问题。 我目前的执行情况如下:

(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)
结果很差

我将尝试简短地解释到目前为止我是如何处理这个问题的:

foldr运行给定集合中的每个元素。对于每个这样的元素,我应该向powerset添加一些新元素。 这些元素应该是什么?为powerset中的每个现有元素添加一个新元素,其中是将列表中的当前元素附加到powerset中的现有元素

这就是为什么我认为我应该以嵌套方式使用foldr两次,一次检查给定列表中的所有项目,对于每个项目,我使用foldr检查“结果”(当前电源集)中的所有项目

我遇到了空列表的问题(没有向powerset添加任何内容),因此添加了“if”部分(不仅仅是foldr),但它也不能很好地工作

我想就是这样。我感觉很接近,但这仍然是非常具有挑战性的,所以每一个帮助都会受到欢迎。
谢谢

解决方案更简单,无需使用双
foldr
,请尝试以下方法:

(define (powerset-fr lst)
  (foldr (lambda (e acc)
           (append (map (lambda (x) (cons e x)) 
                        acc) 
                   acc))
         '(())
         lst))
如果您的解释器定义了或类似的内容,那么解决方案会稍微短一点-结果的顺序会有所不同,但这并不重要:

(define (powerset-fr lst)
  (foldr (lambda (e acc)
           (append-map (lambda (x) (list x (cons e x)))
                       acc))
         '(())
         lst))
无论哪种方式,它都能按预期工作:

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

调用变量和参数
list
rest
,这是一个不好的主意-这些是一些解释器中的内置过程,可能会有名称冲突。一个相当常见的命名约定是使用
xs
表示
x
的列表,使用
xss
表示x的列表。“我想我应该以嵌套的方式使用foldr两次“你在那儿。”使用foldr而不直接递归到
powerset fr
,是否可以做到这一点?我的意思是,你可以使用递归,但不能对我们正在实现的主函数使用。@TomG如果不是对主函数,那么对什么呢?如果不调用函数本身,它就不会是递归!无论如何,问题的本质排除了它。我的意思是你可以创建一个“助手”函数,你可以对它使用递归。但是不要在我们正在实现的主函数上使用递归。当然,完全没有递归是很好的,但我想这是不可能的。我认为实现这一点不需要折叠,只是因为巧合才起作用。幂集是递归定义的$S=\{\}意味着P(S)=\{\}$,否则$e\在S$和$T=S\\{e\}$中,然后$P(S)=P(T)\cup\{T\cup\{e\}:T\在P(T)\}$中。fold只返回递归定义的powerset,其中删除了$lst$中的最后一个$e$,这恰好是$(car lst)$。使用
powerset fr
无法达到使用
foldr
而不是直接显式递归的目的。这是我的第一个句法线索,说明这里出了问题。仔细阅读,这对我来说毫无意义。:)两个代码段的组合函数都没有使用它们的
acc
参数。(??)因此,即使
foldr
(cdrlst)
执行递归,它也会丢弃其结果,并通过直接递归重做所有工作。foldr的这种用法只是取代了cond。但是在丢弃递归结果之后,它会重新进行递归!一定很慢。。。。。。