Scheme - 列表的子集

时间:2011-11-23 05:02:01

标签: list scheme racket

我需要编写一个能够生成给定列表的所有子集的函数。我有一个使用map的递归版本但是为了奖励我被要求创建一个函数来完成它而不使用显式递归,本地或任何抽象列表函数。我可以使用consempty?emptyfirstrestcond。我正处于崩溃的边缘 - 有什么建议吗?我是否应该为需要执行的每个递归使用lambda语句?

3 个答案:

答案 0 :(得分:2)

我非常怀疑你的教授要求你创建或使用Y-Combinator来解决这个问题,但是如果你想尝试这样做,这里有一些可能有用的代码。在外行人看来,y组合器是一种利用lambda演算的力量来制作函数而不必定义任何东西的方法。如果你有一个工作定义(你提到的那样),那么将它转换为lambdas并不是太困难。我将完成这些步骤并尽力在下面解释,但这是一个非常困难的概念,也是函数式编程中最“令人兴奋”的想法之一。

以下是一个通常定义的函数,它将返回给定列表的长度:

;; mylength : [listof Any] -> Number
;; returns the length of xs
(define (mylength xs)
  (if (empty? xs)
      0
      (+ 1 (mylength (rest xs)))))

现在,要开始摆脱定义和显式递归的标准领域,让我们使mylength成为一个lambda表达式,给定一个能够确定列表长度的函数,返回一个长度。给定列表ys

;; ys is a [Listof Any]
;; mylength is a function that returns the length of a list
(λ (ys)
  (cond [(empty? ys) 0]
        [else (+ 1 (mylength (rest ys)))]))

此代码的问题在于我们根本不希望在我们的代码中拥有mylength。为了让你思考正确的方法,请记住这个lambda函数的整个点是返回给定列表的长度。这对于现在来说似乎是一个神秘的信息,但你会在几行中看到我的意思。

无论如何,仅使用lambdas表达此定义的下一步是使length函数成为lambda函数的参数,如下所示:

;; ys is a [Listof Any]
(λ (len ys)
  ;; if ys is empty, return 0.
  (if (empty? ys) 0
      ;; otherwise, call len again, passing len itself as it's 1st argument.
      (+ 1 (len len (rest ys)))))

在代码末尾看到这一行可能会令人困惑:   (+ 1 (len len (rest ys)))))毕竟,len是一个只需要一个列表并返回它的长度的函数,对吧? 错误。我们不拥有一个获取列表并返回其长度的函数 - 我们不能使用像这里提到的第一个mylength函数这样的函数。我们需要一些其他函数,其目的是返回列表的长度。如果你还记得我“加密地”说了几行,

  

请记住,此lambda函数的整个点是返回给定列表的长度

所以,如果这个lambda函数返回给定列表的长度,我们需要的函数返回给定列表的长度......哇。你在想我在想什么吗?

((λ (len ys) (if (empty? ys) 0 
                  (+ 1 (len len (rest ys)))))
  (λ (len xs) (if (empty? xs) 0
                  (+ 1 (len len (rest xs))))) '(your list here))

“什么!?”你可能在想 - “你可以那个!?”

答案,我的朋友,是的。是的你可以。如果你迷路了,请仔细查看上面的代码。让我们调用外部lambda函数func 1和内部lambda函数func 2func 1与我们之前写的功能相同。这部分是有道理的,对吗? func 1使用其他一些函数len和一个列表ys,并尝试返回ys的长度。由于lenfunc 1的目的是func 1自身具有的相同目的,我们可以将基本上func 1的内容传递给len 1}}外部lambda的参数。

为了更好地理解它,让我们将列表'(1)传递给我们新的,奇怪的,lambda怪物:

第一步:

(empty? '(1)) -> FALSE

外部lambda确定'(1)不为空,因此它会评估下一步。

`(+ 1(len len(rest'(1))))

(rest '(1))评估为empty

(+ 1 (len len empty))

和len等于

(λ (len xs) (if (empty? xs) 0 (+ 1 (len len (rest xs))))),因此上述内容扩展为:

((λ (len ys) (if (empty? ys) 0 
                  (+ 1 (len len (rest ys)))))
  (λ (len xs) (if (empty? xs) 0
                  (+ 1 (len len xs)))) empty)

然后empty被推送到外部lambda ys,并评估第一个条件语句:

(empty? ys)

(empty? empty) -> TRUE

因此(len len empty)调用返回0。现在进入下一步:

(+ 1 0)', which evaluates to 1 , which is the length of the list'(1)'。

这是一个非常难以理解的概念,我认为我没有很好地解释这个概念,因为我帮助你完成识别它所需的步骤。如果你对Y-Combinator的工作方式有所了解,我会给你留下足够的印象 - 无论是你理解我的谣言的能力,还是我解释这种令人困惑的概念的能力。

 1. (empty? '(1)) -> FALSE
 2. (+ 1 (len len (rest ys)))

((λ (len ys) (if (empty? '(1)) 0 
                  (+ 1 (len len (rest ys)))))
  (λ (len xs) (if (empty? xs) 0
                  (+ 1 (len len (rest xs))))) '(your list here))

答案 1 :(得分:0)

嗯,没有任何递归就这样做听起来很难。你会被允许使用这里描述的letrec吗? http://docs.racket-lang.org/reference/let.html#(form._((lib._racket/private/letstx-scheme..rkt)._letrec))

如果允许的话,那可以为你做,但是否则我不确定。

答案 2 :(得分:0)

根据你的回答,听起来这里的想法是使用'fold'函数之一,而不是在代码中进行递归调用。如果你对“折叠”足够熟悉,这并非不可能。从本质上讲,您需要首先考虑在流程的每个步骤中需要计算哪些信息,然后再考虑如何将新信息与旧信息相结合来建模流程。

我认为你最好的选择是写下来:

(define (all-subsets l) (foldl ..?.. ..?.. l))

...然后盯着它,直到你能想到组合/帮助函数的好名字。请记住,辅助函数本身可能使用折叠或贴图。为这个帮助函数选择正确的名称是大多数困难/创造性工作的地方。