【问题标题】:Racket how to define a recursive generator like Python?Racket 如何定义像 Python 这样的递归生成器?
【发布时间】:2014-08-13 16:11:46
【问题描述】:

这是一种递归算法,用于生成集合的所有子集。等效的 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,)
()

这是我试过的球拍版本:

#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 文档中好像没有相关的例子 有没有可能,怎么做?

【问题讨论】:

    标签: recursion scheme generator racket powerset


    【解决方案1】:

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

    • 您使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))
    

    两侧cmets:

    • 关于 Oscar 解决方案的小评论:使用 in-generator 可以是 有点令人困惑,因为它不是迭代生成器的方法,而是 相反,它是一种创建生成器并立即迭代的方法 在它上面。

    • JFYI,如果您关心的话,这并不是一个真正的好方法 效率(而不是玩弄发电机)。

    【讨论】:

    • @ 今天我发现您的解决方案更好,因为您的版本可以独立使用,即(定义 g(子集 3))(显示(g))。而且您还提供了很多有用的提示。所以它是你的。
    【解决方案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)
       ()
    

    【讨论】:

    • 这个答案的一个问题是subsets 是一个只能在生成器内部使用的函数。 IOW,直接调用它不会让你开心。
    猜你喜欢
    • 1970-01-01
    • 2021-05-22
    • 2016-05-03
    • 1970-01-01
    • 1970-01-01
    • 2014-10-05
    • 2019-10-18
    • 2012-05-03
    • 2016-10-05
    相关资源
    最近更新 更多