Racket如何定义像Python这样的递归生成器?

时间:2014-08-13 16:11:47

标签: recursion scheme generator racket powerset

这是一个递归算法,可以生成集合的所有子集。等效的Python代码是:

def subsets(s):
    if not s:
        yield ()
    else:
        for e in subsets(s[1:]):
            yield s[:1] + e
            yield e

for s in subsets((1,2,3)):
    print(s)

结果:

>>> 
(1, 2, 3)
(2, 3)
(1, 3)
(3,)
(1, 2)
(2,)
(1,)
()

这是我试过的Racket版本:

#lang racket
(require racket/generator)

(define (subsets x)
  (generator ()
   (let recur ([s x])
     (if (null? s) 
         (yield '())
         (for ([e (in-producer (recur (cdr s)))])
           (yield (cons (car s) e))
           (yield e))))))

(for/list ([j (in-producer (subsets '(1 2 3)))])
    (display j))

但它不起作用:

Welcome to DrRacket, version 6.0.1 [3m].
Language: racket; memory limit: 128 MB.
(). . result arity mismatch;
 expected number of values not received
  expected: 1
  received: 0
  values...:               

在Racket文档中似乎没有相关示例是否可能,如何?

2 个答案:

答案 0 :(得分:3)

你总体上非常接近,有一些小问题:

  • 您使subsets函数获取集合并返回生成器 正确,但后来你决定将身体包裹在recur循环中 没有充分理由......你想要递归调用返回一个 生成器(用作生产者)所以你只需要打电话 subsets

  • 迭代生成器的正确方法是让它返回一些 完成后的已知值,并将其用作停止值。对于 例如,最后添加一个(void)并使用它来停止。

  • 你不应该混合for/listdisplay - 第一个用于 收集结果列表,第二个用于显示值。 切换到for,或者只需删除display即可返回列表 子集。

修复这些提供了工作代码:

#lang racket
(require racket/generator)

(define (subsets s)
  (generator ()
    (if (null? s)
      (yield '())
      (for ([e (in-producer (subsets (cdr s)) (void))])
        (yield (cons (car s) e))
        (yield e)))
    (void)))

(for ([j (in-producer (subsets '(1 2 3)) (void))])
  (displayln j))

两方评论:

  • 关于奥斯卡解决方案的一个小评论:使用in-generator可以是一个 有点混乱,因为它不是迭代生成器的方法但是 相反,它是一种创建生成器并立即迭代的方法 在它上面。

  • JFYI,如果你关心的话,这不是一个很好的方法 效率(而不是与发电机一起使用)。

答案 1 :(得分:2)

我认为程序可以简化一点。以下内容相当于Python中的代码,并注意我们如何使用in-generator

(require racket/generator)

(define (subsets s)
  (if (null? s) 
      (yield '())
      (for ([e (in-generator (subsets (cdr s)))])
        (yield (cons (car s) e))
        (yield e))))

这样称呼:

(for ([e (in-generator (subsets '(1 2 3)))])
  (displayln e))

=> (1 2 3)
   (2 3)
   (1 3)
   (3)
   (1 2)
   (2)
   (1)
   ()