在Ocaml中压缩列表的代码出错

时间:2012-02-10 04:39:01

标签: ocaml flatten

Hello All我试图在Ocaml中压缩列表。我是新手所以请原谅我,如果我的错误是愚蠢的

因此,例如,如果输入是[[1]; [2; 3]; [4]],我应该以[1; 2; 3; 4]结束。

我想要使用的想法如下 使用accumaltor = []遍历右侧列表(使用fold_right) 伪代码如下

func flatten(list,  accumalator) 
  For each item from right to left in list 
     If Item is a scalar then n :: accumalator
     Else fi Item is a list of form head :: tail then
               head :: flatten (tail, accumalator).

我认为理论上算法是正确的,但如果你不同意,请告诉我。

现在我的OCaml代码实现了这个算法

let rec flatten acc x =
    match x with 
           n -> n :: acc
        | [x] -> x :: acc
        | head :: remainder -> 
            head :: ( my_flat acc remainder ) 
 and my_flat = List.fold_right flatten
 ;;

 my_flat [] [[1];[2;3];[4]]

我得到的错误如下      错误:此表达式的类型为“a”,但表达式需要类型          '列表

在匹配语句的最后一个模式中读取head ::(my_flat acc remainder)的行上发生错误

感谢任何帮助。

4 个答案:

答案 0 :(得分:4)

在OCaml中,列表的所有元素必须是相同的类型。因此,值[1; [2; 3]; 4]本身无效。它包含两个类型为int的元素和一个类型为int list的元素。从本质上讲,你对待解决问题的陈述是不可能的。

$ ocaml312
        Objective Caml version 3.12.0

# [1; [2; 3]; 4];;
Characters 4-10:
  [1; [2; 3]; 4];;
      ^^^^^^
Error: This expression has type 'a list
       but an expression was expected of type int

这听起来像是一个家庭作业问题,所以我只是说限制自己在OCaml中有效的列表可能会让它更容易解决。

修改

好的,问题现在可以解决了!

报告的类型错误的本质是这样的。您的累积结果为acc(示例中的int list类型)。您想要将列表x(也是类型int list)添加到其中。您已将x分解为headint)和remainderint list)。如您所见,remainder不适合您的my_flat函数。它需要int list list,即整数列表。实际上,您的递归调用几乎肯定会转到flatten而不是my_flat

我看到的另一个问题:List.fold_right的参数是:函数,列表和起始值。在您对my_flat的测试中,您将以其他顺序提供最后两个。空列表[]是您的起始值。

我希望这足以让你前进。由于您刚刚开始使用OCaml,因此在它运行之前可能会出现另外两个问题。

修改2

以下是一些评论,如果您仍在使用自己的解决方案,这可能会成为剧透......

您的函数my_flat的更整洁版本位于OCaml标准库中,名称为List.flatten。看一下这个实现很有意思:

let rec flatten = function
    [] -> []
  | l::r -> l @ flatten r

我称这是一个非常优雅的解决方案,但不幸的是它并非尾递归。因此它会消耗一些(线性)数量的堆栈空间,甚至可能会崩溃很长的列表。

这里基于相同的想法,使用标准的FP累加器技巧来获得尾递归行为(如Thomas所述):

let flatten2 ll =
    let rec go acc = function
    | [] -> List.rev acc
    | l :: r -> go (List.rev_append l acc) r
in
    go [] ll

通常情况下,尾递归版本以相反的顺序累积结果,并在结束时反转它。

答案 1 :(得分:2)

您可以通过直接编写算法来开始,通过分解输入值的基本情况,即。输入列表为空,或者输入列表的头部为空,或者输入列表的头部有头部和尾部:

let rec flatten = function
  | []          -> []
  | []     :: t -> flatten t
  | (x::y) :: t -> x :: (flatten (y::t))

然后,您可以优化该函数,因为此代码不是尾递归的,因此当列表变得太大时会崩溃。因此,您可以使用常用技术重写此内容:

let flatten list =
  let rec aux accu = function
    | []          -> accu
    | []     :: t -> aux accu t
    | (x::y) :: t -> aux (x::accu) (y::t) in
  List.rev (aux [] list)

所以我的建议是:首先根据输入类型分解问题,然后再使用累加器来优化代码。

答案 2 :(得分:2)

我喜欢这个,辅助函数使用累加器,列表列表的第一个元素以及列表列表的其余部分,对我来说更清楚:

let flatten list =
  let rec aux acc list1 list2 =
    match list1 with
      | x :: tail -> aux (x :: acc) tail list2
      | [] ->
        match list2 with
          | [] -> List.rev acc
          | x :: tail -> aux acc x tail
  in
  aux [] [] list

答案 3 :(得分:1)

感谢您的帮助 这是我用来解决这个问题的代码

let flatten list  =
    let rec flatten_each acc x =
        match x with 
               [] -> acc
        |  head :: remainder -> head :: ( flatten_each acc remainder )
in
List.fold_right flatten_each ( List.rev list ) []
;;

编辑:正如托马斯所指出的,这个解决方案不是尾递归的。尾递归版本在

之下
let flatten list  =
    let rec flatten_each acc x =
        match x with 
               [] -> acc
            |  head :: remainder -> (flatten_each (acc @ [head]) remainder )
    in
     List.fold_right flatten_each list []
;;