我正在用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)]
发布于 2012-11-07 21:35:47
很难说您是否知道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]
#
该函数返回一个新列表,并将值添加到列表的末尾。原始列表不会更改。
顺便说一句,在列表的前面添加值要习惯得多。重复地添加到列表的末尾是很慢的--它会产生二次行为。如果您想要另一种顺序,通常要做的是将所有内容添加到前面,然后反转列表--这仍然是线性的。
编辑
显然,您确实想要一个如下所示的函数:
设f a list = list @ (list,a)
这在现实中是不可能的,类型不能正常工作。一个列表只能包含一种类型的内容。所以你可以得出结论,列表t
的类型与类型(t, v) list
相同,其中v
是a的类型。这是一个递归类型,而不是你真正想要使用的类型(IMHO)。
实际上,您可以使用-rectypes
在OCaml中获取此类型
$ 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
。这在真正的程序中是行不通的,因为在真正的程序中,你需要一个类型来表示你的列表。
作为另一个附注:在列表的开头添加值的美妙之处在于列表的旧值仍然存在。这是新列表的尾部。这看起来更接近你真正想要做的事情。
https://stackoverflow.com/questions/13278457
复制相似问题