方案:高阶程序和递归

时间:2012-10-06 20:52:44

标签: recursion map scheme

我正在使用计划作为我正在学习的课程的一部分。我被告知在我的作业中使用高阶函数。然而,这条指令似乎有点不清楚。

我不完全理解高阶程序的想法。我能够使用递归来完成所有问题,但这是同样的事情。任何人都可以通过递归函数与使用高阶函数编写的函数来解释它。

作为第二个问题:

示例:尝试抓住所有奇数

我可以使用(flatten (map odd ((1 4 5) (4 5 1 4 9)))),但如果有嵌套列表,我可以在嵌套列表上使用map,如:

(flatten (map odd ((1 3 (9 5 7))));是否有这种功能或干净的方式来做它?

1 个答案:

答案 0 :(得分:2)

高阶函数的要点是减少代码中的样板,并减少循环之间的耦合(技术上它是一个递归,但它的目的是循环,所以我将这样引用它)和实际逻辑。这是一个例子(重新抓住所有奇数):手动循环看起来像这样:

(define (filter-odd lst)
  (cond ((null? lst) '())
        ((odd? (car lst)) (cons (car lst) (filter-odd (cdr lst))))
        (else (filter-odd (cdr lst)))))

但请注意,您已经在一个函数中进行了循环和过滤。这使得更难弄清楚函数正在做什么,因为这两个不相关的操作被耦合在一起。使用更高级别的功能,您可以采用不同的方式:

(define (filter pred lst)
  (cond ((null? lst) '())
        ((pred (car lst)) (cons (car lst) (filter pred (cdr lst))))
        (else (filter pred (cdr lst)))))

(define (filter-odd lst)
  (filter odd? lst))

现在注意,odd?如何与循环逻辑分离,循环逻辑现已分为filterfilter现在接受一个函数对象来决定是否保留该项目,filter的调用者可以插入他们选择的任何函数。

这就是高阶函数的含义:它是一个以函数对象作为参数的函数,用于自定义其操作。

如问题的原始编辑中所述,map是另一个高阶函数,但它不是从列表中过滤项目,而是返回原始列表中每个项目的转换,其中特定的转换由map的调用者通过参数给出。


要回答关于展平等的实际问题,(map filter-odd '((1 3 (9 5 7))))将返回包含单个项目的列表,即调用(filter-odd '(1 3 (9 5 7)))的结果。所以不,map不会为您递归到子列表中(filter也不会。)

但是你可以先将输入展平(因为你无论如何都要平坦化输出),然后直接调用filter-odd。我希望这会给你你期望的结果。

(我将您的odd重命名为filter-odd,因为这不太可能与odd?(谓词)混淆。)


奖金材料

顺便说一句,filtermap都是更通用的高阶函数的特化,称为折叠(或更具体地说,右折叠)。折叠可以表达filtermap无法容纳的内容,但是以某种方式涉及遍历列表中的所有项目。以下是length函数的示例,表示为折叠:

(define (foldl func init lst)
  (if (null? lst) init
      (foldl func (func (car lst) init) (cdr lst))))

(define (length lst)
  (foldl (lambda (elem count)
           (+ count 1))
         0 lst))

这里的好处是length函数不必担心遍历列表:由折叠处理。它只需要担心在每次迭代时要做什么(这里只是将{1}添加到count,最开始为0)。

在这种情况下,无论我们从左侧还是右侧移动,长度都是相同的,而在Scheme中,从左侧移动更节省空间,因此我们更喜欢这样做。但是为了实现mapfilter,必须使用右侧折叠(否则元素会反转出来 - 尝试在{x}中使用foldr替换以下函数,你会看到):

foldl