Scheme 用reduce定义map

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的过程中,情况会发生变化

我刚读了约翰·休斯的文章,我正试着用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
的过程中,情况会发生变化

激励示例是
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)))