ocaml - 是否可以在 OCaml 中定义剩余参数?

标签 ocaml

相关线程在 reddit .

在racket中,我们可以用rest argument定义一个函数作为:

(define (avg . l)
    (/ (apply + l) (length l)))

我们可以这样调用这个函数:

> (avg 1 2 3)
2

有很多方法可以解决 reddit 回复中提到的这个特定的 avg

但是如果我想做一些更复杂的事情,比如:

(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 有多少个参数,参数的类型可以是任何类型:整数、列表、字符串。

我想知道 OCaml 中是否有类似的语义?

最佳答案

ML 和 Haskell 并没有真正与 Lisp 的 &rest(或类 Lisp 语言的类似功能)相对应。撇开类型问题不谈,函数的定义方式意味着没有好的方法来定义函数的“剩余参数”。

使用“rest”参数的主要两个应用是可变参数函数和函数包装器。 Reddit 线程已经回答了如何执行可变参数函数(使用列表参数),所以我认为这是关于函数包装器的,这里的事情可能会变得有点棘手。

您遇到的根本问题是,对于不专门使用元组或列表参数的 ML 函数,实际上并不存在参数列表或元组的概念。例如函数

let d x y = abs (x - y)

等价于函数

let d x = (fun y -> abs (x - y))

换句话说,一个 (n+1) 元函数实际上是一个函数,当应用于单个参数时,会产生一个 n 元函数。例如,d 0 返回一个描述距 0 的距离的一元函数。

如果你想对参数元组进行操作,那么你需要这样指定它们:

let d (x, y) = abs (x - y)

然后您可以使用(例如)d(3, 5) 而不是 d 3 5 来调用它。请注意,优化的 OCaml 编译器通常应该为这两种情况生成相同的代码。

您可以很容易地从类型中分辨出来。第一个函数 (d x y) 的类型是

int -> int -> int

而第二个 (d(x, y)) 具有类型

int * int -> int

Arity 在前一种情况下变成了一个非常模糊的概念:我们有一个二元函数返回类型 int 的值还是一个一元函数返回 int -> int?编译器无法分辨,你必须看程序员的意图,所以你必须告诉包装器确切地包装哪些部分。

当你有元组形式的参数时,你可以很容易地定义内存,因为元组只是一个单一的参数。例如,让我们定义 Ackermann 函数,然后对其应用内存:

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

请注意,这个简单示例实际上并未将记忆化应用于递归调用,而仅应用于顶级调用。不过思路应该还是很清晰的。

此外,如果转换涉及非平凡的多态行为,您可能需要一个仿函数而不是函数来表达转换。

通过一些样板,您还可以将其应用于 f x y 表示法。例如,如果您编写了 ack 来接受两个 int 参数而不是一对 int,那么您可以这样写:

let ack m n = memoize (fun (m, n) -> ack m n)

如果你真的雄心勃勃,你甚至可以编写一个 PPX 重写器将其编码为语法扩展点的一部分,这样你就可以编写如下内容:

let%memoize ack x y = ...

关于ocaml - 是否可以在 OCaml 中定义剩余参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35968221/

相关文章:

ocaml - 在ocaml中检查记录大小?

ocaml - ocamldoc 可以引用类型构造函数吗?

types - 在 Ocaml 中重用和扩展定义的类型

compiler-errors - 为什么OCaml具有强制性的独特的float和int文字语法?

generics - 哪种语言的泛型是 OCaml 中类似于 C++、Java 或 C# 的泛型类和函​​数?

algorithm - 内联算法

ocaml - ocamldebug 的 REPL?

ocaml - 更改 utop 输出宽度

c++ - 我的编译器在 C/C++、OCaml 和 F# 之间犹豫不决

constructor - 使用只有一个元组值的变体类型构造函数