Scheme 用reduce定义map
我刚读了约翰·休斯的文章,我正试着用Scheme来继续写下去 他定义的第一个高阶函数是reduce,我在Scheme中定义如下:Scheme 用reduce定义map,scheme,higher-order-functions,Scheme,Higher Order Functions,我刚读了约翰·休斯的文章,我正试着用Scheme来继续写下去 他定义的第一个高阶函数是reduce,我在Scheme中定义如下: (define u-reduce (lambda (ff init lst) (if (null? lst) init (ff (car lst) (u-reduce ff init (cdr lst)))))) 在本文中,我可以使用此函数重新创建reduce的一些应用程序,但是在从reduce移动到map的过程中,情况会发生变化
(define u-reduce
(lambda (ff init lst)
(if (null? lst)
init
(ff (car lst) (u-reduce ff init (cdr lst))))))
在本文中,我可以使用此函数重新创建reduce
的一些应用程序,但是在从reduce
移动到map
的过程中,情况会发生变化
激励示例是doubleall=reduce doubleandcons nil
,其中doubleandcons num list=cons(2*num)list
我不知道如何把这个转化为一个计划。我可以看出,目标结果可以通过以下方式实现:
(define doubleandcons
(lambda (lst)
(if (null? lst)
'()
(cons (* 2 (car lst)) (doubleandcons (cdr lst))))))
(doubleandcons '(1 2 3))
> (2 4 6)
但是,我看不到要传递给reduce
什么以使列表中的每个元素加倍-可能是因为我一直跳转到map
(请参见下面的u-map
)版本作为问题的解决方案
我可以看到init
='()
,但不能看到传递到ff
位置的函数,以使u-reduce
的行为类似于u-map
(define u-map
(lambda (ff lst)
(if (null? lst)
'()
(cons (ff (car lst)) (u-map ff (cdr lst))))))
(u-map (lambda (x) (* 2 x)) '(1 2 3))
> (2 4 6)
这可能吗?或者我没抓住重点 通用Lisp
reduce
是一种通用的折叠
。在Scheme中,您有一个将元素按顺序折叠的右键
。您的u-reduce
就像一个右折叠,其参数顺序相同。因此,以下各项应在R6RS/R7RS中起作用:
(define (my-map fun lst)
(fold-right (lambda (x a) (cons (fun x) a)) '() lst))
正如您所看到的,我在一个匿名函数中使用了passed函数来向右折叠
,同时执行cons
在Scheme和Racket的其他版本中,您可以通过使用获得相同的行为,许多实现都支持它,因此请查看它的文档,了解您是如何导入/需要它的
(define u-map
(lambda (ff lst)
(reduce (lambda (x y) (cons (ff x) y)) '() lst)))
在列表中折叠cons会给您该列表的副本 你应该将你的
reduce
定义为明确的懒惰,因为这篇文章都是关于懒惰的重要性和效用的
通常的reduce
是一个(右)折叠,即一个亚同构(参见Haskell的foldr
),但是让我们为一般性定义一个:
(define (para f z lst)
(if (null? lst)
z
(f lst (car lst) ;; usually, f (cdr lst) (car lst) ...
(lambda () (para f z (cdr lst))))))
(另见)。我们这样使用它:
;; doubleall = reduce doubleandcons nil
;; where doubleandcons num rest = cons (2*num) rest
(define (doubleall lst)
(para doubleandcons '() lst))
(define (doubleandcons lst num rest) ; ignore input lst
(cons (* 2 num) (rest))) ; force the lazy rest
(define (member elt lst)
(para (lambda (xs x r)
(if (equal? x elt) xs (r))) ; conditionally force, or
'() lst)) ; ignore the rest
(define (mymap f lst)
(para
(lambda (xs x r)
(cons (f x) (r))) ; cons the result of calling f w/ x,
'() lst)) ; onto the rest
(define (all pred lst)
(para
(lambda (xs x r)
(and (pred x) (r))) ; possibly short-circuit
#t lst))
您不能与您的版本短路,因为它在将其作为参数传递给组合函数(
ff
)之前先计算其余部分 +1/接受:(u-reduce(lambda(xy)(cons((lambda(x)(*2x))x)y))“()”(123))
正是我需要的桥。你的函数para
看起来有点像CPS,但传递的lambda不是任何函数-为什么?我计划跟随你的链接,但是解释一下这是如何使函数显式懒惰的会很有帮助。只需将表达式封装在lambda中,就可以创建一个函数,在调用时执行它的操作。这被称为延迟执行(一段代码)。在惰性评估模型下,这种情况会自动发生——一切都会延迟。在这里,我们显式地为计算的某一部分执行此操作。不,这不是CPS,因为lambda没有参数。顺便说一句,这种lambda被称为“thunks”。我不理解在递归步骤中传递lst
的必要性。我可以重新编写您的所有示例函数,并在递归步骤中重新编写para
(f(car lst)(lambda()…),如果没有它,任何东西似乎都不会损坏。完全正确!(几乎):lst是准形态“协议”的一部分;没有它,它将是一种变形,即传统褶皱;除了成员
需要lst
之外,它不能用亚同态这么容易地定义(它可以,但只是不那么容易和直接)。这就是为什么我说“为了一般性”。)但大多数情况下都是未使用的。:)在cata和para之间,你是说?但是我在这里展示了实际的区别,在成员的impl中:)对于反同构,我们必须将它扭曲成(define(member-elt-lst)(cata(lambda(xsr)(if(equal?(car-xs)elt)xs(r)))”()(maplist(lambda(x)x)lst))
,使用,(define(maplist-f-lst)(if(null-lst))()(cons(fst)(maplist-f(cdr-lst)))
。