Ocaml 将几个2元组转换为列表列表

Ocaml 将几个2元组转换为列表列表,ocaml,Ocaml,这是一个关于ocaml列表和元组的问题。我有一些整数或浮点数的2元组,我想把它转换成一个包含2个元素的列表。假设我已经定义了一个num类型Int of Int | Float of Float,转换应该给出以下结果: ((1,1.0),(0.4,1),(0,0)) => [[Int 1;Float 1.0];[Float 0.4; Int 1];[Int 0;Int 0]] 或者更准确地说 let a = (1,1.0) and b = (0.4,1) and c = (0,0) in

这是一个关于ocaml列表和元组的问题。我有一些整数或浮点数的2元组,我想把它转换成一个包含2个元素的列表。假设我已经定义了一个num类型Int of Int | Float of Float,转换应该给出以下结果:

((1,1.0),(0.4,1),(0,0)) => [[Int 1;Float 1.0];[Float 0.4; Int 1];[Int 0;Int 0]]
或者更准确地说

 let a = (1,1.0) and b = (0.4,1) and c = (0,0) in
        myconversion (a,b,c) ;;
  => [[Int 1;Float 1.0];[Float 0.4; Int 1];[Int 0;Int 0]]
点是值a,b,c。。。由对元组使用不同签名的人在源文件中的多个位置定义


这里的困难在于我不知道2元组int或float的元素类型,这取决于元组的不同。

您的输入数据不能用您描述的OCaml表示。OCaml是强类型的。例如,您的示例输入列表在OCaml中是无效值:

# [(1,1.0);(0.4,1);(0,0)];;
Error: This expression has type float but an expression was expected of type
         int
因此,你所描述的问题的本质,不知道类型,实际上是不可能的。您必须使用其他一些表示输入的方法。例如,您可以对所有内容使用浮动。或者可以使用成对的字符串

更新

重写的问题的答案是相同的。在OCaml中,不可能不静态地知道某事物的类型;i、 例如,在编写程序时,除非它可以是任何类型。在运行时不可能或不必要查询某个对象的类型。所以你的问题至少在我看来没有答案

对于OCaml,您必须考虑类型系统,而不是反对它。过了一段时间,你开始真的喜欢它,或者至少对我来说是这样的。我首先写下你想要你的函数MyConversion的类型

更新2

我将重复我的建议,将您的输入视为字符串。假设您已经将输入解析为成对的字符串,下面是一些代码,可以满足您的需要:

let myconversion coords =
    let c1 s =
        if String.contains s '.' then
           Float (float_of_string s)
        else
           Int (int_of_string s)
    in
    let cp (a, b) = [c1 a; c1 b] in
    List.map cp coords
以下是它如何用于重新解释为字符串的输入:

# myconversion [("1", "1.0"); ("0.4", "1"); ("0", "0")];;
- : fi list list = [[Int 1; Float 1.]; [Float 0.4; Int 1]; [Int 0; Int 0]]
更新3

下面是一些将数字文件解析为以字符串对表示的坐标的粗略代码。只要输入中的元组格式正确,它就应该工作

let coords fname =
    let ic = open_in fname in
    let len = in_channel_length ic in
    let buf = Buffer.create 128 in
    let () = Buffer.add_channel buf ic len in
    let () = close_in ic in
    let s = Buffer.contents buf in
    let nums = Str.(split (regexp "[^0-9.]+") s) in
    let rec mkcoords sofar = function
    | [] | [_] -> List.rev sofar
    | a :: b :: rest -> mkcoords ((a, b) :: sofar) rest
    in
    mkcoords [] nums

设置中有两个明显的问题:

您不知道元组参数的类型

您希望将它们作为单个n元元组传递

对于问题2,您必须专门为该类型编写一个函数,而您可以通过嵌套两个元组来模拟类型级别的列表类型:

myconversion a,(b,c) ;;
原因是使用该设置,您可以在类型级别列表上编写递归多态函数:

val myconversion : type a b. (a,b) -> num list
不过,最后一个元素仍然存在问题

因此,假设您可以将一个序列传递给转换函数,并让它逐个处理该序列的元素,您仍然需要找到从元组类型中选择正确的成对转换函数的方法:这基本上是即席多态性,也就是说,您需要能够在函数的参数类型1上重载函数。不幸的是,OCaml不支持开箱即用

一种可能是,我可能没有这样做的经验来实现一个扩展,该扩展将提取给定表达式的类型信息,并生成正确的代码来在您自己的代码中处理它

一种灵活的技术是让该扩展生成元组类型的代数描述,并在处理元组的代码中使用该描述作为等式见证:

type _ w =
    | U : (unit * unit) w
    | IF : 'a w -> ((int * float) * 'a) w
    | FI : 'a w -> ((float * int) * 'a) w
    (* other constructors if necessary *)

(* data *)
let a = 1,1.0
let b = 2.0, 2
let c = 3.0, 3
let d = 4, 4.0
let l = a,(b, (c,(d,((),()))))

(* witness *)
let w = IF (FI (FI (IF U)))
  (* the type parameter of w should be the same as l type *)

let rec conv : type a b. (a * b) w -> (a * b) -> num list = fun w (x, xs) ->
    match w with
          U -> []
        | IF w' -> let i,f = x in (Int I)::(Float f)::(conv  w' xs)
        (* etc *)
这里,我们将类型级别的nil列表编码为unit*unit w

coalgebraic方法需要将函数重载注册到扩展内的转换函数多态签名,并让它从函数重载字典中选择正确的函数重载


在LtU网站上有一个关于这个主题的链接。

感谢所有回答的人。我终于找到了一个解决方案,使用了一点魔法:

# type num = Int of int | Float of float;;
# let to_num x = if Obj.is_int (Obj.repr x) then
                   Int (Obj.magic (Obj.repr x) : int)
                 else
                   Float ((Obj.magic (Obj.repr x) : float));;
# let pair_to_num (a,b) = [to_num a; to_num b];;
# let myconversion (a,b,c) = [pair_to_num a; pair_to_num b; pair_to_num c];;
以及测试:

# myconversion ((1,1.0),(0.4,1),(0,0));;
- : num list list = [[Int 1; Float 1.]; [Float 0.4; Int 1]; [Int 0; Int 0]]

# myconversion ((0,0),(1,1.0),(0.4,1));;
- : num list list = [[Int 0; Int 0]; [Int 1; Float 1.]; [Float 0.4; Int 1]]

魔术,顺序不重要,类型被记录下来!然后我可以按照迪迪埃的想法去掉这对多余的括号。

我的问题中确实有一个输入错误,输入是[1,1.0,0.4,1,0,0]。所以它是关于将一个元组转换成一个列表。我正在编辑我的问题。这些未知类型的数字来自哪里?不能在OCaml中生成未知类型的对象。数据是平面中的坐标点。a坐标为1,1.0,b坐标为0.4,1等。我可能需要处理路径a、b、c,。。。或者路径b,c,a。。。有数千个这样的数据点,我认为坐标只是从另一种语言的数据文件中复制粘贴的,这种语言不区分int和float,因此元组的类型不一致。我建议您将它们视为字符串。你可以查看一个字符串,看看它是浮点型还是int型。在那之后,其他的一切都会按照你的要求工作。你必须在OCaml中使用类型系统。虽然您可以在源代码中创建任意值的元组,但它不会是
以一种你可以系统化处理的形式。就我个人而言,我会将数据移动到一个单独的文件中并对其进行解析。关于问题2,这并不重要,因为我知道我有多少元组,为了清晰起见,我编辑了我的问题。尽管如此,你的回答还是很有趣。最后我找到了一个解决方案,记录在答案部分。看起来,至少对于int/float,我们可以用一点魔法来伪装adhoc多态性。当然,我没有考虑运行时表示:幸运的是,这些值没有使用int64,或者两个大小相同的装箱值!请记住,这不是实际的OCaml编码使用Obj.magic意味着您的代码没有保证的意义。