将一些2元组转换为列表列表

时间:2014-07-19 21:49:55

标签: ocaml

这是关于ocaml列表和元组的问题。我有一些2元组的数字(整数或浮点数),我想将它转换为列表列表(包含2个元素)。假设我已经定义了int |的num类型Int漂浮浮动,转换应该给出以下内容:

((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,这取决于元组)。

3 个答案:

答案 0 :(得分:3)

您输入的数据无法在您描述的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中,不可能静态地知道某种东西的类型;即,在您编写程序时(除非它可以是任何类型)。在运行时查询某事物的类型是不可能的(或必要的)。所以你的问题没有答案(至少就我所见)。

对于OCaml,您必须考虑类型系统而不是反对它。过了一段时间,你开始真的喜欢它(或者至少它对我有用)。我首先写下你希望函数myconverstion具有的类型。

更新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

答案 1 :(得分:1)

您的设置中存在两个明显的问题:

  1. 您不知道元组参数的类型

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

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

    myconversion a,(b,c) ;;
    

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

    val myconversion : type a b. (a,b) -> num list
    

    但最后一个元素仍然存在问题。

    因此,假设您可以将序列传递给转换函数,并让它逐个处理该序列的元素,您仍然需要找到一种从元组类型中选择对转换的正确函数的方法:这基本上是ad-hoc多态,即。你需要能够在参数'的类型(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

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


    在LtU网站上有​​关于该主题的discussion

答案 2 :(得分:1)

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

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

魔术,顺序没关系,类型被录制!然后我可以按照他们的想法去除那对多余的括号。