首页 文章

OCaml - 向该列表添加一个包含元组列表的新元组

提问于
浏览
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) 的元组列表中 . 当我尝试将新函数添加到函数列表中时(让's assume, that it' s当前未定义,我不必覆盖任何东西),我反复迭代到函数列表的末尾,并且想要添加一个新函数元组 .

问题是,假设当前函数列表的类型为 (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)]

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]
    #
    

    该函数返回一个新列表,其值添加到结尾 . 原始列表不会更改 .

    作为旁注,将值添加到列表前面更加惯用 . 反复添加到列表的末尾很慢 - 它给出了二次行为 . 如果你想要另一个订单,通常要做的就是将所有内容添加到前面然后反转列表 - 这仍然是线性的 .

    Edit

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

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

    这实际上不可能,类型不正确 . 列表可以包含仅一种类型的内容 . 因此,您可以得出结论,列表 t 的类型与 (t, v) list 类型相同,其中 v 是a的类型 . 这是一种递归类型,而不是你真正想要使用的东西(恕我直言) .

    您实际上可以使用 -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>
    #
    

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

    Edit 2

    现在我看一下,你的第一个代码示例避免了需要递归类型,因为你指定了两个不同的列表副本 . 在使用相同列表调用函数之前,这些可能是不同的类型 . 所以函数类型不是递归的 . 当您使用相同列表的两个副本进行调用时,将创建一个新值,其类型为's different than the type of the list. It only works because you',对于不同的值(具有不同类型)使用相同的名称 l . 它赢了't work in a real program, where you' d需要一个代表你的列表的类型 .

    另一方面评论:在列表开头添加值的好处在于列表的旧值仍然存在 . 这是新名单的尾部 . 这似乎更接近你可能想要做的事情 .

相关问题