3

我正在用一些简单的命令在 OCaml 中编写一个交互式计算器。用户应该能够定义自己的简单函数(数学函数),例如

let f(x) = x
let g(x) = 2*f(x)

现在,函数应该像函数式语言一样处理,这意味着它们应该记住它们的创建时间环境。这意味着,对于一个函数,我必须保持其环境的闭包,即函数和变量。

我将当前定义的函数保存在一个像(functions_present_at_the_time_of_creation, variables_present_at_the_time_of_creation, function_name, function_argument_names, function_formula). 当我尝试将一个新函数添加到函数列表中时(假设它当前未定义并且我不必覆盖任何内容),我反复迭代到函数列表的末尾并希望添加一个新的元组。

问题是,假设我当前的函数列表是类型(a*b*c*d*e) list,当我尝试将一个带有自身的元组添加到它的末尾时,它会将其类型更改为((a*b*c*d*e) list*f*g*h*i) list. 我能做些什么才能执行这样的列表添加到自身,封装在一个元组中?

这是我在尝试找到解决此问题的方法时编写的一些简单的 SSCCE。

let rec add_to_end list list_copy dummy = match list with
| [] -> [(list_copy, dummy)]
| h::t -> h::(add_to_end t list_copy dummy) 

let add list dummy = add_to_end list list dummy

这个尝试使用列表的副本来完成。以下是在没有使用副本的情况下编写的(当然,这两个示例都不起作用):

let rec add_to_end list dummy = match list with
| [] -> [(list, dummy)]
| h::t -> h::(add_to_end t dummy) 

第一个示例在尝试使用函数 add 时不起作用,但是当以这种方式(在解释器中)执行此操作时:

let l = [];;
let l = add_to_end l l 1;;
let l = add_to_end l l 2;;
let l = add_to_end l l 3;;

然后它工作正常。我会很感激任何帮助,我也可能会考虑改变设计,非常欢迎任何建议。

编辑:这是上述命令的输出:

# let l = [];;
val l : 'a list = []
# let l = add_to_end l l 1;;
val l : ('a list * int) list = [([], 1)]
# let l = add_to_end l l 2;;
val l : (('a list * int) list * int) list = [([], 1); ([([], 1)], 2)]
# let l = add_to_end l l 3;;
val l : ((('a list * int) list * int) list * int) list =
[([], 1); ([([], 1)], 2); ([([], 1); ([([], 1)], 2)], 3)]
4

1 回答 1

3

很难说你是否知道 OCaml 列表是不可变的。您不能将值添加到现有列表的末尾。现有列表永远无法更改。您可以创建一个新列表,并在末尾添加一个值。如果你这样做,我不明白你为什么要在由列表和新值组成的末尾添加一对。我怀疑你想错了。这是一个函数,它接受一个列表和一个整数并将整数添加到列表的末尾。

# let rec addi i list =
    match list with
    | [] -> [i]
    | h :: t -> h :: addi i t
  ;;
val addi : 'a -> 'a list -> 'a list = <fun>
# let x = [1;2;3];;
val x : int list = [1; 2; 3]
# addi 4 x;;
- : int list = [1; 2; 3; 4]
# x;;
- : int list = [1; 2; 3]
# 

该函数返回一个新列表,并将值添加到末尾。原始列表没有改变。

作为旁注,将值添加到列表的前面更为惯用。反复添加到列表的末尾很慢——它给出了二次行为。如果你想要其他顺序,通常的做法是将所有内容添加到前面,然后反转列表——这仍然是线性的。

编辑

显然你真的想要一个看起来像这样的函数:

让 fa list = list @ [(list, a)]

这实际上是不可能的,类型不正确。一个列表只能包含一种类型的东西。所以可以断定list的类型和typet是一样的(t, v) list,这里v是a的类型。这是一种递归类型,而不是您真正想要使用的东西(恕我直言)。

您实际上可以使用以下方法在 OCaml 中获得这种类型-rectypes

$ ocaml -rectypes
        OCaml version 4.00.0

# let f a list = list @ [(list, a)];;
val f : 'a -> (('b * 'a as 'c) list as 'b) -> 'c list = <fun>
# 

但是(正如我所说)这是我会避免的。

编辑 2

现在我看了一下,您的第一个代码示例避免了需要递归类型,因为您指定了列表的两个不同副本。在您使用相同的列表调用函数之前,这些可能是不同的类型。所以函数类型不是递归的。当您使用同一个列表的两个副本调用时,您会创建一个类型与列表类型不同的新值。它之所以有效,是因为您l对不同的值(具有不同的类型)使用相同的名称。它在真正的程序中不起作用,您需要一个代表您的列表的类型。

As another side comment: the beauty of adding values to the beginning of a list is that the old value of the list is still there. It's the tail of the new list. This seems lot closer to what you might actually want to do.

于 2012-11-07T21:35:47.193 回答