如果我想要反转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 '()
答案 0 :(得分:5)
MIT Scheme在其基本库中不包含fold
函数,仅fold-left
和fold-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
参数的原因恰恰是您发现的原因:它使fold
与cons
一起使用变得更加容易 - 就像接受积累的价值作为他们的第二个论点。
顺便说一下,您的原始问题似乎已经混淆了fold-left
和fold-right
:fold-right
返回列表不变,fold-left
返回倒置的一对。如果您了解fold-left
和fold-right
的定义方式,那么这是有道理的。
以某种方式,fold-left
和fold-right
都会从 left 开始折叠列表,因为Scheme列表是单链接的,无法从对。不同之处在于左折叠是迭代的,而右折叠是递归的。
左侧折叠逐个将过程应用于每个元素,然后将结果线程化到下一个应用程序中。当作为嵌套应用程序查看时,这最终会颠倒元素的顺序:
(proc eN ... (proc e1 (proc e0 init)) ...)
相反,右侧折叠以递归方式应用过程,使嵌套应用程序中的顺序保持一致:
(proc e0 (proc e1 ... (proc eN init) ...))
与cons
一起使用时,左侧折叠会反转,但右侧折叠只是标识。
(这在Haskell这样的懒惰语言中可能更有趣,因为正确折叠并不依赖于整个列表,尽管它应该从“右边”开始,因此它可以在无限流上运行......但是,它的相关性要低得多在Scheme。)