使用相同的辅助函数使用foldr和foldl反转列表

时间:2015-09-08 01:26:12

标签: list scheme reverse fold

1. (define (rev1 ls) (foldr g '() (map f ls))) 
2. (define (rev2 ls) (foldl g '() (map f ls))) 

我需要定义fg,以便rev1rev2生成给定列表ls的反向,其他定义为遵循

(define (foldl op z ls) 
   (if (null? ls) 
       z 
       (foldl op (op z (car ls)) (cdr ls))))

(define (snoc x y) (cons y x))

(define (foldr op z ls)
     (if (null? ls)
        z
        (op (car ls) (foldr op z (cdr ls)))))

我不确定如何定义fg,以便1和2都生成参数列表的反向。

2 个答案:

答案 0 :(得分:0)

这是一个相当人为的练习,但无论如何 - 这些程序应该有效:

(define f list)

(define (g e acc)
  (append acc e))

例如:

(rev1 '(1 2 3 4 5))
=> '(5 4 3 2 1)

(rev2 '(1 2 3 4 5))
=> '(5 4 3 2 1)

答案 1 :(得分:0)

有趣的问题。你可以用递归思维和等式推理来攻击它(在代码中用等于等于等号)。

首先,重写:

(rev1 ls) 
 = (letrec ( (rev1 (lambda (ls)
                     (foldr g '() (map f ls)) )) ) 
     (rev1 ls))

 = (letrec ( (rev1 (lambda (ls)
                     (foldr g '() (map f ls)) )) ) 
     (foldr g '() (map f ls)))

 = (letrec ( (rev1  (lambda (ls)
                      (foldr g '() (map f ls)) )) 
             (foldr (lambda (op z ls)
                     (if (null? ls)
                       z
                       (op (car ls) (foldr op z (cdr ls)))) )) ) 
     (foldr g '() (map f ls)))

 = (letrec ( (foldr (lambda (op z ls)
                     (if (null? ls)
                       z
                       (op (car ls) (foldr op z (cdr ls)))) ))
             (rev11 (lambda (ls)
                      (if (null? ls)
                        '()
                        (g (f (car ls)) (foldr g '() (map f (cdr ls))))) )) ) 
     (rev11 ls))

 = (letrec ( (rev11 (lambda (ls)
                      (if (null? ls)
                        '()
                        (g (f (car ls)) (rev11 (cdr ls)))) )) ) 
     (rev11 ls))

因为(map f (cons x xs)) == (cons (f x) (map f xs))

类似地,

(rev2 ls)
 = (letrec ( (rev2 (lambda (ls)
                     (foldl g '() (map f ls)) )) )
     (rev2 ls))

 = (letrec ( (rev2  (lambda (ls)
                      (foldl g '() (map f ls)) ))
             (foldl (lambda (op z ls) 
                      (if (null? ls) 
                        z 
                        (foldl op (op z (car ls)) (cdr ls))) )) )
     (foldl g '() (map f ls)))

 = (letrec ( (rev22 (lambda (z ls) 
                      (if (null? ls) 
                        z 
                        (rev22 (g z (f (car ls))) (cdr ls))) )) )
     (rev22 '() ls))

(完成第二次推导中缺少的步骤)。

因此我们得出了新的定义

(define (rev11 ls)    (if (null? ls)    ; (rev1 ls) == (rev11 ls)
                        '()
                        (g (f (car ls)) (rev11 (cdr ls)))) )

(define (rev22 z ls)  (if (null? ls)    ; (rev2 ls) == (rev22 '() ls)
                        z 
                        (rev22 (g z (f (car ls))) (cdr ls))) )

现在我们可以应用一些递归思维。假设rev11执行它应该做的事情 - 撤消它给出的列表。因此,对于列表ls == [a,b,c,...,n]

(rev1 ls)
  = (rev11 ls)
  = (rev11 [a,b,c,...,n])
  = (g (f a) (rev11 [b,c,...,n]))
  = (g (f a) [n,...,c,b])    ; by assumption
  ; must be equal to
  = [n,...,c,b,a]

我们如何将a和列表xs合并为一个新列表,与xs的{​​{1}}结尾相同?

a

因此 = (append [n,...,c,b] (list a)) f == list。我们已经达到了Óscar López's answer中所见的定义!

现在我们必须看看(g x y) == (append y x)发生了什么。如果rev22f不同,则表示原始问题没有解决方案。

g

很容易看到,同样的(rev2 ls) = (rev22 '() ls) = (rev22 '() [a,b,c,...,n]) = (rev22 (g '() (f a)) [b,c,...,n]) = (rev22 (g (g '() (f a)) (f b)) [c,...,n]) = ... f也适用。

非巧合的是,g相当于append-reverseCommon Lisp's revappend,具有翻转的参数顺序。

编写这两个定义的另一种更多伪编码方式,其中rev22代表[]'()代表{x}和{{1} } (f x),是

x + y

现在解决方案似乎几乎是不言而喻的!

请注意,您的(g x y)定义使用了(rev1 [a,b,...,n]) = {a} + ({b} + ({c} + (... + ({n} + [])...))) = {a} + (rev1 [b,...,n]) (rev2 [a,...,m,n]) = (...((([] + {a}) + {b}) + {c}) + ...) + {n} = (rev2 [a,...,m]) + {n} 运算符的Haskell's个参数顺序。 In Racket订单被翻转 - foldl作为 last 参数,而不是 first 。因此,方程将成为

g

并且没有翻转参数顺序的第二个等式z。然后修改后的拼图似乎无法解决。