是否可以在OCaml中定义rest参数? [英] Is it possible to define rest argument in OCaml?
问题描述
在 reddit 中关联线程.
在球拍中,我们可以使用休息参数为:
In racket, we could define a function with rest argument as:
(define (avg . l)
(/ (apply + l) (length l)))
我们可以通过以下方式调用此函数:
We could call this function by:
> (avg 1 2 3)
2
有很多方法可以解决reddit答复中提到的特定avg
.
There are plenty of ways to solve this particular avg
mentioned by the replies from reddit.
但是,如果我想做一些更复杂的事情,例如:
But if I want to do something more complicated like:
(define *memoize-tbl* (make-hasheq))
(define (bind fn . args)
(let ([res (apply fn args)])
(hash-set! *memoize-tbl*
(equal-hash-code (cons fn args))
res)
res))
(define (f1 loi i s)
(+
(length loi)
i
(string-length s)))
(bind f1 '(1 2 3) 8 "hi")
我们可以看到函数bind
不在乎fn
具有的参数数量,并且参数的类型可以是任何东西:整数,列表,字符串.
We can see that the function bind
does not care the number of arguments fn
has , and the type of arguments could be anything: integer, list, string.
我想知道OCaml中是否也有类似的语义?
I was wondering if maybe there is semantic similarly in OCaml?
推荐答案
ML和Haskell确实没有Lisp的&rest
(或类似Lisp的语言的类似功能)的对应物.除了类型问题之外,定义函数的方式意味着没有好的方法来定义函数的剩余参数".
ML and Haskell don't really have a counterpart to the &rest
of Lisp (or similar features of Lisp-like languages). Type issues aside, the way that functions are defined means that there is no good way to define "the remaining arguments" of a function.
使用"rest"参数的主要两个应用是可变参数函数和函数包装器. Reddit线程已经回答了如何执行可变参数函数(使用list参数),所以我认为这是关于函数包装器的,这里的事情可能会有些毛茸茸.
The primary two applications of using "rest" arguments are variadic functions and function wrappers. The Reddit thread already answered how to do a variadic function (use a list argument), so I presume this is about function wrappers, and here things can get a bit hairy.
您所面临的潜在问题是,对于不专门使用元组或列表参数的ML函数,参数列表或元组的概念实际上并不存在.例如,功能
The underlying problem that you have is that the concept of a list or tuple of arguments does not really exist for ML functions that do not specifically use tuple or list arguments. For example, the function
let d x y = abs (x - y)
等效于功能
let d x = (fun y -> abs (x - y))
换句话说,(n + 1)元函数实际上是一个函数,当将其应用于单个参数时,会产生n元函数.例如,d 0
返回一元函数,该函数描述了距0
的距离.
In other words, an (n+1)-ary function is actually a function that, when applied to a single argument, yields an n-ary function. For example, d 0
returns a unary function that describes the distance from 0
.
如果要对参数元组进行操作,则需要这样指定它们:
If you want to operate on tuples of arguments, then you need to specify them as such:
let d (x, y) = abs (x - y)
然后可以使用(例如)d(3, 5)
而不是d 3 5
来调用它.请注意,优化的OCaml编译器通常在两种情况下都应生成相同的代码.
You can then call this with (e.g.) d(3, 5)
instead of d 3 5
. Note that the optimizing OCaml compiler should normally generate identical code for both cases.
您可以轻松分辨出类型之间的区别.第一个函数(d x y
)具有类型
You can easily tell the difference from the types. The first function (d x y
) has the type
int -> int -> int
而第二个(d(x, y)
)具有类型
int * int -> int
在前一种情况下,Arity变成了一个非常模糊的概念:我们是否有一个返回类型为int
的值的二进制函数或一元函数返回了一个int -> int
的值?编译器无法告诉您,您必须查看程序员的意图,因此必须准确告知包装器要包装哪些部分.
Arity becomes a really fuzzy concept in the former case: do we have a binary function returning a value of type int
or a unary function returning a value of int -> int
? The compiler can't tell, you have to look at the programmer's intent, so you have to tell a wrapper exactly which parts to wrap.
当您具有元组形式的参数时,您可以轻松定义备注,因为元组只是一个参数.例如,让我们定义Ackermann函数,然后对其应用备注:
When you have arguments in tuple form, you can easily define memoization, because a tuple is just a single argument. For example, let's define the Ackermann function and then apply memoization to it:
let rec ack = function
| (0, n) -> n + 1
| (m, 0) -> ack (m-1, 1)
| (m, n) -> ack (m-1, ack(m, n-1))
let memoize f =
let memo_table = Hashtbl.create 0 in
let f' x = try
Hashtbl.find memo_table x
with Not_found -> begin
let y = f x in Hashtbl.add memo_table x y; y
end in f'
let ack = memoize ack
请注意,这个简单的示例实际上并未将备忘录应用于递归调用,而仅将其应用于顶级调用.但是,这个想法应该仍然很清楚.
Note that this simple example does not actually apply memoization to the recursive call, but only to the top-level invocation. However, the idea should still be clear.
此外,如果转换涉及非平凡的多态行为,则可能需要函子而不是函数来表达转换.
Also, if the transformation involves non-trivial polymorphic behavior, you may need a functor instead of a function to express the transformation.
使用一些样板文件,您也可以将其应用于f x y
表示法.例如,如果编写ack
来接受两个int参数而不是一对int,则可以编写:
With a bit of boilerplate, you can also apply this to the f x y
notation. For example, if you had ack
written to accept two int arguments rather than a pair of ints, you could then write:
let ack m n = memoize (fun (m, n) -> ack m n)
如果您真的很雄心勃勃,您甚至可以编写一个PPX重写器,将其编码为语法扩展点的一部分,以便可以编写类似以下内容的东西:
And if you felt really ambitious, you could even write a PPX rewriter to encode this as part of a syntax extension point so that you can write something like:
let%memoize ack x y = ...
这篇关于是否可以在OCaml中定义rest参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!