从 Curry-0, 1, 2 到 ...n [英] Going from Curry-0, 1, 2, to ...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 返回一个过程,所以你可以看到 loop
将 always 返回一个 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 apply
ing 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屋!