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)的行上发生错误
感谢任何帮助。
答案 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
分解为head
(int
)和remainder
(int 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 []
;;