Functional programming 了解方案中的左折和右折

Functional programming 了解方案中的左折和右折,functional-programming,scheme,fold,Functional Programming,Scheme,Fold,因此,我的任务是在Scheme中使用fold left或fold right实现最基本版本的“map”函数和“filter”函数。我很难理解这些函数到底在做什么。以下是我所拥有的: (define (myMap f l) (fold-left (lambda (f a) (f a)) '() l)) (define (myFilter f l) (fold-left f '() l)) 最底层是我直觉上认为应该是的。对l的每个元素应用一个筛选器(比如number?,并将结果放

因此,我的任务是在Scheme中使用fold left或fold right实现最基本版本的“map”函数和“filter”函数。我很难理解这些函数到底在做什么。以下是我所拥有的:

(define (myMap f l)
    (fold-left (lambda (f a) (f a)) '() l))

(define (myFilter f l)
    (fold-left f '() l))
最底层是我直觉上认为应该是的。对l的每个元素应用一个筛选器(比如number?,并将结果放入空列表)。顶部是完全错误的,但我觉得这更在正确的轨道上。使用某种lambda函数将函数应用于数字

下面是我正在寻找的输出示例:

(myMap sqrt '(4 9 16 25)) ; (2 3 4 5)
(myFilter odd? '(1 2 3 4 5)) ; (1 3 5)

考虑一下
fold
函数的含义:它得到一个两参数函数,我们称之为“迭代器”和一个列表,并将迭代器重复应用于列表的元素,方式如下:在泛型步骤中,使用列表的当前元素调用函数,以及迭代器先前应用的结果(在第一步中,先前的结果只是
fold
的第三个参数)

因此,例如,
右折
从列表的末尾开始,在每个“步骤”迭代器都以这种方式应用:

(iterator current-element previous-result) ;; produces: next result
将结果作为正确的参数“馈送”到下一个应用程序。类似地,对于左折叠,不同之处在于,函数从列表的开头开始应用,并使用前面的结果作为左参数

这样,如果您必须以这种方式定义<代码> map <代码>,请考虑您必须构建一个必须以上述方式应用的函数“迭代器”。也就是说,函数必须对当前元素应用

f
,并生成将在下一次迭代中使用的结果。由于我们希望构建此应用程序的结果列表,迭代器的主体可以是:

(cons (f current-element) previous-result)
整个功能变成:

(define (myMap f lst)
  (define (iterator current-element previous-result)
    (cons (f current-element) previous-result))
  (fold-rigth iterator '() lst))
由于这是一个练习,我将把
myFilter
的定义留给您:这次您必须找到一个合适的迭代器体,这样当前元素只有在应用于它的函数返回true时才会“插入”到结果中,否则它必须被忽略


另一个有趣的练习是对这两个函数使用
左折
。由于迭代器的应用顺序不同,函数要稍微复杂一些。

折叠表示一种预设的递归模式。Left fold从左到右遍历列表中的所有元素,使用组合当前元素和当前累加器的函数转换累加器,以生成累加器的下一个值,该值将在下一次调用组合函数时使用,如

(fold-left <+> [a,b,c..,n] z) === (n <+> (... <+> (c <+> (b <+> (a <+> z)))...))
右折叠是类似的,尽管它将当前元素与右侧递归处理的结果相结合

(右折[a,b,c…,n]z)==(a(b(c(…(nz)…))

在这两种情况下,组合函数在这里实际上是相同的,
cons
操作的函数组成和
f
函数本身。但其中一个会产生相反的结果(哪一个?)。您可以通过调用
reverse

的后处理步骤来解决这一问题
左折
右折
通过从第一个元素到最后一个元素的缩减过程来缩减一个或多个列表,但应用顺序保持在
右折
中,而在
左折
中则相反。如果您执行以下操作,则很容易显示:

#!r6rs
(import (rnrs))

;; helper for R6RS fold-left since argument order is swapped
(define (xcons d a) 
  (cons a d))

(fold-left xcons '() '(1 2 3 4)) ; ==> (4 3 2 1)
(fold-right cons '() '(1 2 3 4)) ; ==> (1 2 3 4)
我之所以使用
xcons
是因为对于
左折叠
来说,累加器是第一个参数。在
fold left
中,equivalent仅称为
fold
,其参数顺序与
fold right
相同:

(import (rnrs base)
        (only (srfi :1) fold fold-left))

(fold cons '() '(1 2 3 4))       ; ==> (4 3 2 1)
(fold-right cons '() '(1 2 3 4)) ; ==> (1 2 3 4)
左折叠是尾部递归的,因为它处理第一个元素,并成为下一次迭代的累加器。右折叠需要先将最后一个元件压缩到蓄能器上,然后再将最后一个元件压缩到第一个元件上。这意味着,如果可能,应避免右折叠,在许多情况下,如果结果的顺序或向下折叠到单个值(例如,查找最大元素),左折叠是可以的

map
filter
的情况下,您希望结果的顺序是相同的,因此您需要始终使用
右折

对于
map
,您需要制定一个程序,该程序将
cons
提供的程序应用于带有累加器的元素的结果。这就是fold right最终需要的列表。您当前的解决方案没有任何
cons
,因此您将无法获得列表

对于
过滤器
,如果谓词的结果是真值,则需要执行一个过程,将原始元素约束到累加器中,如果不是真值,则只计算累加器


因为我认为这是我的家庭作业,所以我会让你去做实际的实施。快乐黑客。

这里的诀窍是,与
map
filter
不同,
fold
不一定返回列表。折叠操作比映射和过滤更一般(可以根据折叠定义它们)。例如,通过使用折叠将列表(“reduce”是折叠的常用替代名称)缩减为总和,可能更容易理解折叠的作用。试试
(向左折叠+0'(123445))
看看我的意思。@AlexisKing这就是我在很多文档中找到的例子。我知道它会像
(…(+(+(+01)2)3)
那样应用它,但是我如何扩展它来处理任意函数呢?我猜类似于
(lambda(fln)(append(fn)l))
这样我就可以将我应用函数的元素添加回列表了?或者这不合理吗?问题是提供给p的元素
#!r6rs
(import (rnrs))

;; helper for R6RS fold-left since argument order is swapped
(define (xcons d a) 
  (cons a d))

(fold-left xcons '() '(1 2 3 4)) ; ==> (4 3 2 1)
(fold-right cons '() '(1 2 3 4)) ; ==> (1 2 3 4)
(import (rnrs base)
        (only (srfi :1) fold fold-left))

(fold cons '() '(1 2 3 4))       ; ==> (4 3 2 1)
(fold-right cons '() '(1 2 3 4)) ; ==> (1 2 3 4)