你没有提到你的代码没有输入。
您的第一个函数子句只有变量l1
,用于递归。然而在这里它被用作三元组的第一个元素,作为参数给出。这与 SML 使用的 Hindley-Milner 类型系统并不真正齐头并进。以下非正式想法可能会更好地说明这一点:
让我们首先假设l1
具有类型'a
,因此该函数必须接受该类型的参数并返回未知的东西'a -> ...
。但是,在右侧,您创建一个(l1, [], [])
必须具有 type的参数'a * 'b list * 'c list
。但是由于它是作为参数传递给函数的,这也必须意味着'a
等于'a * 'b list * 'c list
,显然不是这样。
显然这不是你的初衷。似乎您的意图是拥有一个以列表作为参数的函数,然后同时拥有一个递归辅助函数,该函数需要两个额外的累积参数,即原始列表中的正数和负数列表。
为此,您至少需要为您的辅助函数指定另一个名称,这样它的定义就不会重新绑定原始函数的定义。然后你有一些选项,关于这个辅助函数应该在哪个范围内。一般来说,如果除了从“主”函数调用这个辅助函数没有任何意义,那么它不应该放在“主要”功能之外的范围。这可以使用这样的 let 绑定来完成:
fun positive xs =
let
fun positive' ys p n = ...
in
positive' xs [] []
end
这样辅助函数positives'
就不能在positive
函数之外调用。
有了这个照顾,您的原始代码就会出现更多问题。
由于您只返回正整数列表,因此无需跟踪负整数。
您应该使用模式匹配来分解列表元素。这样您就无需使用列表的头部和尾部,也无需验证列表中是否确实存在头部和尾部。
fun foo [] = ... (* input list is empty *)
| foo (x::xs) = ... (* x is now the head, and xs is the tail *)
不应该使用附加运算符 ( @
),只要您可以避免它(您总是可以)。问题是当您在左侧有一个巨大的列表而在右侧有一个小列表时,它的运行时间很糟糕(这通常是右侧的情况,因为它主要用于附加一个单个元素)。因此,通常应该认为使用它是不好的做法。
然而,有一个非常简单的解决方案,即始终连接列表前面的元素(以相反的顺序构建列表),然后在将列表作为最后一件事返回时将其反转(使其按预期顺序排列) ):
fun foo [] acc = rev acc
| foo (x::xs) acc = foo xs (x::acc)
鉴于这些小注释,我们最终得到了一个看起来像这样的函数
fun positive xs =
let
fun positive' [] p = rev p
| positive' (y::ys) p =
if y < 0 then
positive' ys p
else
positive' ys (y :: p)
in
positive' xs []
end