【问题标题】:Scheme: high order procedure and recursion方案:高阶过程和递归
【发布时间】:2012-10-07 13:05:32
【问题描述】:

我正在使用方案作为我正在学习的课程的一部分。我被告知在作业中使用高阶函数。不过这条指令似乎有些不清楚。

我不完全理解高阶程序的概念。我可以使用递归来解决所有问题,但这是同一回事。谁能用一个递归函数的例子来解释它,而不是用一个高阶过程编写的例子。

作为第二个问题:

示例:尝试抓取所有奇数

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

(flatten (map odd ((1 3 (9 5 7)))) ;是否有此功能或干净的方法?

【问题讨论】:

    标签: recursion map scheme


    【解决方案1】:

    高阶函数的意义在于减少代码中的样板,并减少循环(技术上是递归,但它的目的是循环,所以我将其称为循环)和循环之间的耦合实际逻辑。这是一个示例(重新抓取所有奇数):手动循环如下所示:

    (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,右折叠是必要的(否则元素会颠倒——尝试在下面的函数中用foldl 替换foldr,你会看到):

    (define (foldr func init lst)
      (if (null? lst) init
          (func (car lst) (foldr func init (cdr lst)))))
    
    (define (map func lst)
      (foldr (lambda (elem result)
               (cons (func elem) result))
             '() lst))
    
    (define (filter pred lst)
      (foldr (lambda (elem result)
               (if (pred elem)
                   (cons elem result)
                   result))
             '() lst))
    

    【讨论】:

    • 非常感谢,我已经掌握了大部分内容,我会再读一遍以确保:) 真的很有帮助。
    猜你喜欢
    • 1970-01-01
    • 2014-08-16
    • 2021-11-19
    • 1970-01-01
    • 2020-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多