在Racket中扩展形式的折叠

时间:2016-08-18 12:24:27

标签: scheme racket

来自http://www.cse.unsw.edu.au/~en1000/haskell/hof.html的示例:

(foldr / 7 (list 34 56 12 4 23))
(foldl / 7 (list 34 56 12 4 23))

球拍中的输出:

5 193/196
5 193/196

在这种情况下,foldl和foldr的完整(扩展)形式是什么?它不是以下:

> (/ (/ (/ (/ (/ 7 34) 56) 12) 4) 23)
1/300288

编辑:我修改了上述问题,因为在另一个问题Why is foldl defined in a strange way in Racket?中已经解释了在Racket vs Haskell中实现fold的实现。

编辑:如果我清楚地理解答案,可以使用“线程”模块非常清楚地显示扩展表单,其中语句按执行顺序出现(_表示前一语句的输出):

foldl:

(require threading)
; expanded form of (foldl / 7 (list 34 56 12 4 23))
; FROM LEFT TO RIGHT: 
(~> 7
    (/ 34 _)
    (/ 56 _)
    (/ 12 _)
    (/ 4  _)
    (/ 23 _) )

foldr:

; expanded form of (foldr / 7 (list 34 56 12 4 23))
; FROM RIGHT TO LEFT: 
(~> 7
    (/ 23 _)
    (/ 4  _)
    (/ 12 _)
    (/ 56 _)
    (/ 34 _) )

两种情况下的输出都是相同的:

5 193/196
5 193/196

在以下示例中,它给出了正确的答案(对于foldl和foldr来说是不同的):

; FROM LEFT TO RIGHT:
(foldl - 0 '(1 2 3 4))
(~> 0
    (- 1 _)  ; 1-0=1
    (- 2 _)  ; 2-1=1
    (- 3 _)  ; 3-1=2
    (- 4 _)) ; 4-2=2

; FROM RIGHT TO LEFT: 
(foldr - 0 '(1 2 3 4))
(~> 0
    (- 4 _)  ; 4-0=4
    (- 3 _)  ; 3-4=-1
    (- 2 _)  ; 2-(-1)=3
    (- 1 _)) ; 1-3=-2

输出:

2
2
-2
-2

用通用语言表示:

The sent function takes 2 arguments, 

the first argument is from the list, one after the other 
(left to right or right to left depending on foldl and foldr), 

the second argument is init first and 
then the output of previous calculation.

2 个答案:

答案 0 :(得分:2)

在DrRacket中,在foldl上按鼠标右键并选择“打开定义文件”在提供列表中再次右键单击并选择“跳转到下一个绑定的出现”。你会看到这个:

(define foldl
  (case-lambda
    [(f init l)
     (check-fold 'foldl f init l null)
     (let loop ([init init] [l l])
       (if (null? l) init (loop (f (car l) init) (cdr l))))]
    [(f init l . ls)
     (check-fold 'foldl f init l ls)
     (let loop ([init init] [ls (cons l ls)])
       (if (pair? (car ls)) ; `check-fold' ensures all lists have equal length
           (loop (apply f (mapadd car ls init)) (map cdr ls))
           init))]))

然而,因为你只有一个列表,所以这是第一个术语,如果lambda是当前的,第一行检查参数并抛出异常。您可以将其简化为:

(define (foldl f init l)
  (let loop ([init init] [l l])
    (if (null? l)
        init
        (loop (f (car l) init) (cdr l))))

使用替代规则:

(foldl / 7 '(34 56 12 4 23))                                  ;==>
(loop 7 '(34 56 12 4 23))                                     ;==>
(loop (/ (car '(34 56 12 4 23)) 7) (cdr '(34 56 12 4 23)))    ;==>
(loop (/ (car '(56 12 4 23)) (/ (car '(34 56 12 4 23)) 7)) (cdr '(56 12 4 23)))    ;==>
(loop (/ (car '(12 4 23)) (/ (car '(56 12 4 23)) (/ (car '(34 56 12 4 23)) 7))) (cdr '(12 4 23)))    ;==>
(loop (/ (car '(4 23)) (/ (car '(12 4 23)) (/ (car '(56 12 4 23)) (/ (car '(34 56 12 4 23)) 7)))) (cdr '(4 23)))    ;==>
(loop (/ (car '(23)) (/ (car '(4 23)) (/ (car '(12 4 23)) (/ (car '(56 12 4 23)) (/ (car '(34 56 12 4 23)) 7))))) (cdr '(23)))    ;==>
(/ (car '(23)) (/ (car '(4 23)) (/ (car '(12 4 23)) (/ (car '(56 12 4 23)) (/ (car '(34 56 12 4 23)) 7)))))    ;==>
(/ 23 (/ 4 (/ 12 (/ 56 (/ 34 7)))))    ;==>
5 193/196

我会留下foldr作为练习。

关于弃牌和标准

#!racket中的折叠是特定于球拍的。在Scheme中,更精确地#!r6rs您拥有fold-leftfold-right,而且与#!racket不同,从左到右的参数顺序会发生变化,使其更接近* new Haskell版本

SRFI-1列表库使用名称foldfoldr,并期望两者的参数顺序相同,就像#!racket一样。 SRFI-1还支持不同的长度列表,并在最短的列表中停止,因此它是具有最多功能的列表。 SRFI-1可以包含#!racket (require srfi/1)#!r6rs(import (rnrs :1))

答案 1 :(得分:1)

Haskell的foldrfoldl并不完全等同于Racket's。此外,div是整数除法,因此您应该在Racket中使用quotient。但即便如此,

(foldr quotient 7 (list 34 56 12 4 23)) => 8
(foldl quotient 7 (list 34 56 12 4 23)) => quotient: undefined for 0

您可以仔细阅读有关foldl和foldr如何工作的文档,但我想参考teaching languages的文档:

(foldr f base (list x-1 ... x-n)) = (f x-1 ... (f x-n base))
(foldl f base (list x-1 ... x-n)) = (f x-n ... (f x-1 base))

所以它变成

(quotient 34 (quotient 56 (quotient 12 (quotient 4 (quotient 23 7)))))
(quotient 23 (quotient 4 (quotient 12 (quotient 56 (quotient 34 7)))))