麻省理工学院计划中的折叠程序有哪些方向?

时间:2015-11-04 07:09:23

标签: scheme reduce fold mit-scheme

如果我想要反转MIT Scheme中的列表,我可以使用(fold cons '() list)执行此操作,以便如果列表为(define lis '(1 2 3 4)),则(fold cons '() lis)会提供(4 3 2 1)。有两种折叠,左右折叠,但如果我使用(fold-right cons '() lis),我会得到(1 2 3 4)所以普通折叠不能是那个。此外,如果我使用(fold-left cons '() lis),我会获得((((() . 1) . 2) . 3) . 4),因此也不能成为原始示例中的折叠。反转清单需要什么样的折叠?

我问,因为我希望能够制作一个通用的折叠:

(define ls '((1 2 3) (4 5 6)))
(gen-fold cons '() ls)
=> ((6 5 4) (3 2 1))
(define l2 '(((1 2) (2 3)) ((3 4) (4 5))))
(gen-fold cons '() ls)
=> (((5 4) (4 3)) ((3 2) (2 1)))

编辑:为了更好地呈现问题,这些图表描述了我认为在调用'(fold cons '() '(1 2 3))时发生的列表的轮换,假设fold是修改后的fold-left为每个亚历克西斯国王:

(define lis (list '(1 2 3))

     cons
    /   \
   1    cons
        /  \
       2   cons
           /  \
          3   '()

(cons lis '())

         cons
        /  \
      cons '()
     /  \
    1   cons
       /   \
      2    cons
           /  \
          3   '()

(fold-left (cons lis '()))

        cons
       /    \
    cons     cons
   /  \      /  \
  2   cons  1   '()
      /  \
     3   '()


        cons
       /    \
    cons     cons
   /  \       /  \
  3   '()    2    cons
                  /   \
                1     '()

   cons
  /  \
 3   cons
     /  \
    2   cons
        /  \
       1   '()

1 个答案:

答案 0 :(得分:5)

MIT Scheme在其基本库中不包含fold函数,仅fold-leftfold-right。但是,确实存在由SRFI-1声明的fold函数,它由MIT Scheme支持并展示您描述的行为。

fold函数是左折叠 - 也就是说,它通过从左到右的迭代累积列表 - 但它与MIT Scheme的fold-left函数的不同之处在于累加器的参数顺序程序被翻转。也就是说,fold将提供的过程与累加器参数 last 一起应用,而fold-left将过程与累加器参数首先应用

为了说明这一点,我们可以根据fold来定义fold-left

(define (fold proc init lst)
  (fold-left (lambda (x acc) (proc acc x)) init lst))

根据我的经验,fold参数顺序在Lisp实现中更常见,而fold-left参数顺序在其他函数语言中更常见。出于{em> last 提供acc参数的原因恰恰是您发现的原因:它使foldcons一起使用变得更加容易 - 就像接受积累的价值作为他们的第二个论点。

顺便说一下,您的原始问题似乎已经混淆了fold-leftfold-rightfold-right返回列表不变,fold-left返回倒置的一对。如果您了解fold-leftfold-right的定义方式,那么这是有道理的。

以某种方式,fold-leftfold-right都会从 left 开始折叠列表,因为Scheme列表是单链接的,无法从对。不同之处在于左折叠是迭代的,而右折叠是递归的。

左侧折叠逐个将过程应用于每个元素,然后将结果线程化到下一个应用程序中。当作为嵌套应用程序查看时,这最终会颠倒元素的顺序:

(proc eN ... (proc e1 (proc e0 init)) ...)

相反,右侧折叠以递归方式应用过程,使嵌套应用程序中的顺序保持一致:

(proc e0 (proc e1 ... (proc eN init) ...))

cons一起使用时,左侧折叠会反转,但右侧折叠只是标识。

(这在Haskell这样的懒惰语言中可能更有趣,因为正确折叠并不依赖于整个列表,尽管它应该从“右边”开始,因此它可以在无限流上运行......但是,它的相关性要低得多在Scheme。)