fun p(L) =
[L] @ p( tl(L) @ [hd(L)] );
如果L是[1,2,3],那么我想要[[1,2,3],[2,3,1],[3,1,2]]。
因为每次我将第一个数字追加到结尾时,如果L = []则[]在这里不起作用。
如果有三个列表,如何停止该功能?
答案 0 :(得分:2)
您可以在函数中使用参数x来跟踪递归中的深度级别。
fun p(L, x) =
if x < length(L) then [L] @ p(tl(L) @ [hd(L)], x+1)
else [];
然后用x = 0调用该函数。
p([1, 2, 3], 0)
如果你不喜欢额外的参数,那么你可能知道你可以定义另一个函数并使其等于p函数,参数强制为0。
fun p0(L) = p(L, 0);
p0([1, 2, 3]); (* same result as p([1, 2, 3], 0); *)
答案 1 :(得分:1)
让我展示一些更多的实现变体。 首先,让我们定义一个辅助函数,它将列表1位置向左旋转:
(* operates on non-empty lists only *)
fun rot1_left (h :: tl) = tl @ [h]
然后p
函数可以定义如下:
fun p xs =
let
(* returns reversed result *)
fun loop [] _ _ = []
| loop xs n res =
if n = 0
then res
else loop (rot1_left xs) (n-1) (xs :: res)
in
List.rev (loop xs (length xs) [])
end
通常更好(性能方面)在列表的开头添加新元素,然后反转结果列表一次,而不是多次附加到结尾。 注意:这个版本在最后进行了一次虚假旋转,我可以优化它,但没有,使代码更清晰。
我们计算了给定列表的长度以使其旋转“副本”,但我们不必事先遍历xs
,我们可以在旋转时进行。因此,我们可以使用xs
作为一种计数器,以递归方式调用loop
列表尾部的xs
辅助函数。
fun p xs =
let
(* returns reversed result *)
fun loop [] _ _ = []
| loop xs [] res = res
| loop xs (_::tl) res =
loop (rot1_left xs) tl (xs :: res)
in
List.rev (loop xs xs [])
end
完成后,我们现在更接近将p
作为foldl
函数实施:
fun p xs =
(List.rev o #1)
(List.foldl
(fn (_, (res, rot)) => (rot::res, rot1_left rot))
([], xs)
xs)
List.foldl
函数的第二个参数是我们的“累加器”,它在此表示为当前(部分)结果的对,与前面的实现一样,当前旋转名单。这解释了(List.rev o #1)
部分:我们需要获取累加器的第一个组件并将其反转。至于([], xs)
部分 - 当前结果在开头是空的(因此[]
),我们开始旋转初始xs
列表。此外,_
中的(_, (res, rot))
表示给定xs
的当前元素,我们并不关心它,因为它只是作为一个计数器(参见上一个变体)。
注意:o
代表标准ML中的功能组合。