学习计划,试图实施地图

时间:2014-05-10 16:16:51

标签: scheme fold

我尝试在Scheme中使用fold实现map。 我引用了这个网站:[ref1] http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Towards_a_Standard_Library 我理解fold =>的方式 left-associate fold(foldl)从第一个元素折叠/迭代。 (向前) 右联合折叠(foldr)从最后一个元素折叠/迭代。 (向后) [Q1]我理解正确吗?

从ref1:foldl,foldr

的实现
 `1) foldl (left-associate fold)`
  (define (foldl func accum lst)
   (if (null? lst)
        accum
        (foldl func (func accum (car lst)) (cdr lst))))

2) foldr (right-associate fold)
 (define (foldr func end lst)
   (if (null? lst)
      end
      (func (car lst) (foldr func end (cdr lst)))))

[=>]使用fold实现地图,ref1也有带折叠代码的地图,所以它就是..

1) map implementation using foldr from ref1.
  (define (map func lst)      
     (foldr (lambda (x y) (cons (func x) y)) '() lst))

**[Q2] map impelementation using foldl from myself** 
  (define (fold-map-left pr list)
    (foldl 
       (lambda (sublist element) (cons (pr element) sublist))
       '() 
       list)
  what I get as the output...
  e.g. (fold-map-left (lambda(n) (expt n n)) '(1 2 3 4)) 
       output => (256 27 4 1): backwards 
  • 我知道左联合折叠折叠/向后迭代,所以它将采取最后一个并将其放入输出...,那么我该怎样做才能使输出正确顺序?我的代码中还有其他错误吗?

1 个答案:

答案 0 :(得分:1)

首先,我认为你的foldl实施有点偏,在Scheme中实现它的通常方法是(但请务必阅读@JoshuaTaylor的评论,以了解后果):

(define (foldl func accum lst)
  (if (null? lst)
      accum
      (foldl func
             (func (car lst) accum) ; here's the change
             (cdr lst))))

关于第一个问题:两个折叠从左到右使用输入列表,但它们在构建输出结果的方式上有所不同:foldl在开头累积第一个输入元素,然后在结果的顶部累积第二个元素,依此类推;如果我们将列表构建为输出,那么第一个元素将是最后一个(并使用此示例测试您自己的foldl实现,以了解为什么我说它有点偏离):

(foldl cons '() '(1 2 3 4 5))
=> '(5 4 3 2 1)

另一方面,foldr递归地前进,直到它到达空列表然后,随着递归开始展开,最后一个元素首先被累积,然后在结果顶部倒数第二个,等等 - 从而保持输入的相同顺序:

(foldr cons '() '(1 2 3 4 5))
=> '(1 2 3 4 5)

关于第二个问题:在map方面实施foldr会更简单,因为正如我们之前看到的那样,它保留了输入顺序。这与您的相同,但具有更好的格式和变量名称:

(define (map func lst)
  (foldr (lambda (ele acc)
           (cons (func ele) acc))
         '()
         lst))

但是,当然,我们可以使用foldl并在结尾处反转结果(或者:在将列表传递给foldl之前反转列表):

(define (map func lst)
  (reverse
   (foldl (lambda (ele acc)
            (cons (func ele) acc))
          '()
          lst)))

同时拥有foldlfoldr的想法是什么?这是一种权衡(并且从性能的角度来看,请务必阅读@ ChrisJester-Young对实施问题的讨论的评论)。在构建输出时,foldr将保留与输入相同的顺序,但它将生成一个递归过程,该过程在与输入大小成比例的空间中运行:注意func已执行调用递归调用之后。

另一方面,foldl将以相反的顺序构建输出,但它将生成一个在恒定空间中运行的迭代过程:注意在调用递归调用后没有什么可做的,所以它是tail-recursive和Scheme经过优化,可以有效地处理这种递归。