Scheme 语言 列表折叠 foldr 与 foldl 结果不同 的原因分析

Schemeamuwap 发布于 4 天前 2 次阅读


阿木博主一句话概括:Scheme【1】 语言中 foldr【2】 与 foldl【3】 结果不同的原因分析及代码实现【4】

阿木博主为你简单介绍:
在函数式编程语言 Scheme 中,foldr 和 foldl 是两种常用的列表折叠【5】操作。尽管它们的功能相似,但 foldr 和 foldl 的结果可能不同。本文将深入分析 foldr 与 foldl 结果不同的原因,并通过代码实现来展示这一现象。

一、
列表折叠(List Folding)是一种将列表中的元素通过某种操作(如求和、求积、连接等)组合成单一值的操作。在 Scheme 语言中,foldr 和 foldl 是两种常见的列表折叠操作。foldr 从列表的尾部开始折叠,而 foldl 则从列表的头部开始折叠。尽管它们的功能相似,但 foldr 和 foldl 的结果可能不同。本文将分析这一现象的原因,并通过代码实现来展示。

二、foldr 与 foldl 的基本原理
1. foldr
foldr 是从列表的尾部开始折叠,其语法如下:
scheme
(foldr op acc list)

其中,op 是一个二元操作符【6】,acc 是初始值【7】,list 是要折叠的列表。foldr 的折叠过程如下:
- 如果列表为空,则返回初始值 acc。
- 否则,将列表的最后一个元素与 acc 应用 op 操作符,然后将结果与剩余的列表继续折叠。

2. foldl
foldl 是从列表的头部开始折叠,其语法如下:
scheme
(foldl op acc list)

其中,op 是一个二元操作符,acc 是初始值,list 是要折叠的列表。foldl 的折叠过程如下:
- 如果列表为空,则返回初始值 acc。
- 否则,将 acc 与列表的第一个元素应用 op 操作符,然后将结果与剩余的列表继续折叠。

三、foldr 与 foldl 结果不同的原因分析
foldr 和 foldl 结果不同的原因主要在于它们对列表的遍历顺序【8】不同。以下通过一个具体的例子来分析:

假设有一个列表 (1 2 3 4),我们使用求和操作符【9】 + 来折叠这个列表。

1. foldr 的折叠过程:
scheme
(foldr + 0 (1 2 3 4))

- 初始值 acc 为 0。
- 第一步:0 + 4 = 4。
- 第二步:4 + 3 = 7。
- 第三步:7 + 2 = 9。
- 第四步:9 + 1 = 10。

最终结果为 10。

2. foldl 的折叠过程:
scheme
(foldl + 0 (1 2 3 4))

- 初始值 acc 为 0。
- 第一步:0 + 1 = 1。
- 第二步:1 + 2 = 3。
- 第三步:3 + 3 = 6。
- 第四步:6 + 4 = 10。

最终结果为 10。

在这个例子中,foldr 和 foldl 的结果相同。在某些情况下,foldr 和 foldl 的结果可能不同。以下是一个例子:

假设有一个列表 (1 2 3 4),我们使用求积操作符【10】 来折叠这个列表。

1. foldr 的折叠过程:
scheme
(foldr 1 (1 2 3 4))

- 初始值 acc 为 1。
- 第一步:1 4 = 4。
- 第二步:4 3 = 12。
- 第三步:12 2 = 24。
- 第四步:24 1 = 24。

最终结果为 24。

2. foldl 的折叠过程:
scheme
(foldl 1 (1 2 3 4))

- 初始值 acc 为 1。
- 第一步:1 1 = 1。
- 第二步:1 2 = 2。
- 第三步:2 3 = 6。
- 第四步:6 4 = 24。

最终结果为 24。

在这个例子中,foldr 和 foldl 的结果相同。如果我们将列表改为 (1 0 3 4),则 foldr 和 foldl 的结果将不同:

1. foldr 的折叠过程:
scheme
(foldr 1 (1 0 3 4))

- 初始值 acc 为 1。
- 第一步:1 4 = 4。
- 第二步:4 3 = 12。
- 第三步:12 0 = 0。
- 第四步:0 1 = 0。

最终结果为 0。

2. foldl 的折叠过程:
scheme
(foldl 1 (1 0 3 4))

- 初始值 acc 为 1。
- 第一步:1 1 = 1。
- 第二步:1 0 = 0。
- 第三步:0 3 = 0。
- 第四步:0 4 = 0。

最终结果为 0。

在这个例子中,foldr 和 foldl 的结果相同。如果我们将列表改为 (1 2 0 4),则 foldr 和 foldl 的结果将不同:

1. foldr 的折叠过程:
scheme
(foldr 1 (1 2 0 4))

- 初始值 acc 为 1。
- 第一步:1 4 = 4。
- 第二步:4 0 = 0。
- 第三步:0 2 = 0。
- 第四步:0 1 = 0。

最终结果为 0。

2. foldl 的折叠过程:
scheme
(foldl 1 (1 2 0 4))

- 初始值 acc 为 1。
- 第一步:1 1 = 1。
- 第二步:1 2 = 2。
- 第三步:2 0 = 0。
- 第四步:0 4 = 0。

最终结果为 0。

在这个例子中,foldr 和 foldl 的结果相同。如果我们将列表改为 (1 2 3 0),则 foldr 和 foldl 的结果将不同:

1. foldr 的折叠过程:
scheme
(foldr 1 (1 2 3 0))

- 初始值 acc 为 1。
- 第一步:1 0 = 0。
- 第二步:0 3 = 0。
- 第三步:0 2 = 0。
- 第四步:0 1 = 0。

最终结果为 0。

2. foldl 的折叠过程:
scheme
(foldl 1 (1 2 3 0))

- 初始值 acc 为 1。
- 第一步:1 1 = 1。
- 第二步:1 2 = 2。
- 第三步:2 3 = 6。
- 第四步:6 0 = 0。

最终结果为 0。

在这个例子中,foldr 和 foldl 的结果不同。这是因为 foldr 在遇到第一个 0 时立即停止折叠,而 foldl 则继续折叠直到遇到 0。

四、代码实现
以下是一个 Scheme 代码示例,展示了 foldr 和 foldl 的实现:

scheme
(define (foldr op acc list)
(if (null? list)
acc
(op (car list) (foldr op acc (cdr list)))))

(define (foldl op acc list)
(if (null? list)
acc
(foldl op (op acc (car list)) (cdr list))))

(define (sum-foldr list)
(foldr + 0 list))

(define (sum-foldl list)
(foldl + 0 list))

(define (product-foldr list)
(foldr 1 list))

(define (product-foldl list)
(foldl 1 list))

;; 测试代码
(define test-list '(1 2 3 4))
(define test-list2 '(1 0 3 4))
(define test-list3 '(1 2 0 4))
(define test-list4 '(1 2 3 0))

(display "Sum (foldr): ") (display (sum-foldr test-list)) (newline)
(display "Sum (foldl): ") (display (sum-foldl test-list)) (newline)
(display "Product (foldr): ") (display (product-foldr test-list)) (newline)
(display "Product (foldl): ") (display (product-foldl test-list)) (newline)
(display "Sum (foldr): ") (display (sum-foldr test-list2)) (newline)
(display "Sum (foldl): ") (display (sum-foldl test-list2)) (newline)
(display "Product (foldr): ") (display (product-foldr test-list2)) (newline)
(display "Product (foldl): ") (display (product-foldl test-list2)) (newline)
(display "Sum (foldr): ") (display (sum-foldr test-list3)) (newline)
(display "Sum (foldl): ") (display (sum-foldl test-list3)) (newline)
(display "Product (foldr): ") (display (product-foldr test-list3)) (newline)
(display "Product (foldl): ") (display (product-foldl test-list3)) (newline)
(display "Sum (foldr): ") (display (sum-foldr test-list4)) (newline)
(display "Sum (foldl): ") (display (sum-foldl test-list4)) (newline)
(display "Product (foldr): ") (display (product-foldr test-list4)) (newline)
(display "Product (foldl): ") (display (product-foldl test-list4)) (newline)

五、结论
本文分析了 Scheme 语言中 foldr 和 foldl 结果不同的原因,并通过代码实现展示了这一现象。foldr 和 foldl 的结果可能不同,主要原因是它们对列表的遍历顺序不同。在实际应用中,应根据具体需求选择合适的折叠操作。