例如,我遇到了一个问题,需要将元组列表转换为扁平列表:
[((1,2),(3,4),(5,6)]可以变成[1,2,3,4,5,6]
我试图编写这样的函数:
fun helper2(nil,b) = []
| helper2(a,nil) = []
| helper2(a::l1,b::l2) =l1::l2
fun flatten2 [] = []
| flatten2 ((a,b)::tl) = helper2(a,b)
显示:
val flatten2 = fn : ('a list * 'a list list) list -> 'a list list
并且当我尝试使用命令flatten2[(1,2),(3,4),(5,6)];
运行它时它会给我以下错误信息:
stdIn:1.2-1.29 Error: operator and operand do not agree [overload conflict]
operator domain: ('Z list * 'Z list list) list
operand: ([int ty] * [int ty]) list
in expression:
flatten2 ((1,2) :: (3,4) :: (<exp>,<exp>) :: nil)
我的问题是:
感谢
第一个问题:关于为什么类型以('a list * 'a list list)
出现是因为类型推断正在看这部分代码:
| helper2(a::l1,b::l2) =l1::l2
^^
here
请记住,“ cons”(::
)运算符的类型为'a -> 'a list -> 'a list
,它将单个元素粘贴到相同类型元素的列表上。因此SML得出结论,无论l1
和l2
是什么,其关系是l2
是l1
的列表。
fun helper2(nil,b) = []
表示a
必须是列表,因为nil
的类型为'a list
。因此,l2
必须是列表列表(某些类型为'a
)。
问题2和3:我不太确定如何在编写代码时对其进行更正。我可能会写这样的东西:
fun helper2 [] accum = List.rev accum
| helper2 ((a,b)::tl) accum = helper2 tl (b :: a :: accum);
fun flatten2 list = helper2 list [];
helper2
完成所有肮脏的工作。如果输入列表为空,那么我们就完成了,我们可以返回我们一直在建立的反向accumulator。第二种情况是我们实际上将东西添加到累加器中。我们在列表的头和尾进行模式匹配。此模式匹配表示输入的类型为('a * 'a) list
(两个元素均为相同类型的元组列表)。在头部,我们有一个元组,分别命名第一个和第二个元素a
和b
。我们先将a
加上b
放在累加器上,然后递归调用列表末尾的helper2
。最终,我们将仔细检查列表中的所有元素,然后仅剩下累加器,回想起来,它具有所有元素,但顺序相反。调用List.rev
会反转累加器,这就是我们的答案。
并且当我加载并运行它时,我得到这个:
- flatten2 [(1,2), (3,4), (5,6)];
val it = [1,2,3,4,5,6] : int list
为什么SML将a和b值视为列表,而不仅仅是a和b
克里斯已经深入回答了。
您正在将a
作为第一个参数传递给helper2
,后者需要一个列表作为其第一个参数。然后,您将b
作为第二个参数传递给helper2
,该参数使用第二个参数b::l2
(也是一个列表)作为列表的结尾,其中a
是头。因此b
必须是这些列表的列表。
这没有任何意义,很可能是语法混乱的结果:您在a
中传递了您对单个元素b
和flatten2
的看法,但是当您在[ C0]现在是lists,其头分别称为helper2
和a
。那些不是相同的b
和a
。
如何修改我的代码,以便SML可以将a和b视为'a和'b未列出
您可以放弃辅助函数的开头:
b
具有帮助器功能的目的是使它可以在递归期间累积结果,因为此版本的fun flatten2 [] = []
| flatten2 ((a,b)::pairs) = a :: b :: flatten2 pairs
使用大量的堆栈空间。它可以使用一个额外的参数来做到这一点,因此flatten2
无需提及:
这是克里斯制作的版本。
如何使此代码按应有的方式工作?
您可以通过多种方式编写此代码。提到了两种使用显式递归的方法。
这里是使用高阶函数的一些替代方法:
flatten2