是否可以在OCaml中定义rest参数? [英] Is it possible to define rest argument in OCaml?

查看:71
本文介绍了是否可以在OCaml中定义rest参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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屋!

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