从 Curry-0, 1, 2 到 ...n [英] Going from Curry-0, 1, 2, to ...n

查看:60
本文介绍了从 Curry-0, 1, 2 到 ...n的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继上一个关于编写咖喱函数的问题之后,如何创建像球拍一样的咖喱函数,我已经开始为 0、1、2 编写固定大小写 - 它们与教堂数字非常相似,非常简洁.这是我目前所拥有的:

Following up from a previous question I asked about writing a curry function, How to create a make-curry function like racket has, I've started writing the fixed case for 0, 1, 2 -- they are very similar to Church Numerals which is pretty neat. Here is what I have so far:

(define (curry-0 func)
  func)

(define hello (begin (display "Hello!") (newline)))
(curry-0 hello)
; Hello!

(define (curry-1 func)
  (lambda (x)
      (func x )))

((curry-1 -) 2)
; -2

(define (curry-2 func)
  (lambda (x)
    (lambda (y)
      (func x y))))

(((curry-2 +) 2) 3)
5

模式似乎是:

(define curry-n func
     (lambda (x1)
        (lambda (x2)
           ...
            (lambda (xn)
                (func x1 x2 ... xn)

但是,我在构建"n 嵌套 lambda 表达式时遇到了一些麻烦.我想我可以用类似的东西构建一个嵌套的 lambda:

However, I'm having some trouble 'building up' the n-nested lambda expression. I suppose I could build a nested lambda with something like:

(define (curry-n num func)
     (if (num > 0)
         (lambda (?) (curry-n (- num 1) func))))

但我不确定如何将变量绑定"到每个 lambda 函数,以及如何最终将这些相同的变量(按顺序)传递给 (func x1 x2 ... xn) 函数.

But I'm not sure how I would 'bind' the variables to each lambda function and also how I would end up passing those same variables (in order) to the (func x1 x2 ... xn) function.

这是不正确的,但我已经开始...

This is incorrect but I've started along the lines of...

(define (curry-n num func)
 (if (> num 0)
      ; but lambda won't accept a number, possible to string-format?
      (curry-n (- num 1) (lambda (num)))))

这怎么可能?

推荐答案

使用循环

你需要某种loop来从lambda中收集每个arg -

You need some sort of loop to collect each arg from the lambda -

(define (curry-n num f)
  (let loop
    ((n num)
     (args null))
    (lambda ((arg null))
      (cond ((= n 0)
             (apply f (reverse args)))
            ((= n 1)
             (apply f (reverse (cons arg args))))
            (else
             (loop (- n 1) (cons arg args)))))))

curry 应该 always 返回一个过程,所以你可以看到 loopalways 返回一个 lambda,即使对于 num = 0 情况.每个 arg 都被 cons 连接到 args 上,创建了一个反向的参数列表.这就是为什么我们在 apply 用户提供的过程 f 之前反转args.

curry should always return a procedure, so you can see loop will always return a lambda, even for the num = 0 case. Each arg is cons'd onto args, creating a reversed list of arguments. This is why we reverse the args before applying the user-supplied procedure, f.

它是这样工作的 -

(define (hello)
  (println "hello world"))

((curry-n 0 hello))

"hello world"

((((curry-n 3 +) 10) 20) 30)

60


使用分隔的延续

本着分享学习练习的精神,请花一些时间使用 分隔延续 -

In the spirit of sharing learning exercises, take some time to review this example using delimited continuations -

(require racket/control)

(define (curry-3 f)
  (reset (f (shift k k) (shift k k) (shift k k))))

(define (hello a b c)
  (printf "hello world ~a ~a ~a\n" a b c))

((((curry-3 hello) 10) 20) 30)

hello world 10 20 30

要获得 curry-n,我们需要做的就是构建一个 n 延续的列表!

To get curry-n all we need to do is build a list of n continuations!

(require racket/control)

(define (curry-n n f)
  (reset (apply f (build-list n (lambda (_) (shift k k))))))

它就像我们的第一个一样 -

And it works just like our first one -

(define (hello a b c)
  (printf "hello world ~a ~a ~a\n" a b c))

((((curry-n 3 hello) 10) 20) 30)

"hello world 10 20 30"

((((((curry-n 5 +) 10) 20) 30) 40) 50)

150

您可以将这个过程想象成这样的工作,其中每个 __ 都是一个洞";填写 -

You can visualize the process as working something like this, where each __ is a "hole" to fill -

(f __ __ __)
    \
     \_ (lambda (x)
          (f x __ __))
                \
                 \_ (lambda (y)
                      (f x y __))
                              \
                               \_ (lambda (z)
                                    (f x y z))

唯一的区别是要填充n个孔,所以我们构建了一个n孔的列表,然后apply -

The only difference is there are n holes to fill, so we build a list of n holes and apply -

(build-list 3 (lambda (_) (shift k k)))

(list __
       \
        \_ (lambda (x)
              (list x __
                       \
                        \_ (lambda (y)
                             (list x y __
                                        \
                                         \_ (lambda (z)
                                              (list x y z))

(apply f (list x y z)

方案

我没有注意到你在 Scheme 中做这项工作.我们可以定义 build-list -

I didn't notice you were doing this work in Scheme. We can define build-list -

(define (build-list n f)
  (let loop
    ((m 0))
    (if (>= m n)
        '()
        (cons (f m) (loop (+ m 1))))))

我们可以使用 Olivier Danvy 的 移位/重置,

And we can use Olivier Danvy's original implementation of shift/reset,

(define-syntax reset
  (syntax-rules ()
    ((_ ?e) (reset-thunk (lambda () ?e)))))

(define-syntax shift
  (syntax-rules ()
    ((_ ?k ?e) (call/ct (lambda (?k) ?e)))))

(define *meta-continuation*
  (lambda (v)
    (error "You forgot the top-level reset...")))

(define abort
  (lambda (v)
    (*meta-continuation* v)))

(define reset-thunk
  (lambda (t)
    (let ((mc *meta-continuation*))
      (call-with-current-continuation
        (lambda (k)
      (begin
        (set! *meta-continuation* (lambda (v)
                    (begin
                      (set! *meta-continuation* mc)
                      (k v))))
        (abort (t))))))))

(define call/ct
  (lambda (f)
    (call-with-current-continuation
      (lambda (k)
    (abort (f (lambda (v)
            (reset (k v)))))))))

我们的curry-n可以保持不变-

(define (curry-n n f)
  (reset (apply f (build-list n (lambda (_) (shift k k))))))

((((((curry-n 5 +) 10) 20) 30) 40) 50)

150

这篇关于从 Curry-0, 1, 2 到 ...n的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆