在这种情况下如何停止递归函数(SML)

时间:2016-06-03 18:53:07

标签: sml smlnj ml

fun p(L) =
    [L] @ p( tl(L) @ [hd(L)] );

如果L是[1,2,3],那么我想要[[1,2,3],[2,3,1],[3,1,2]]。

因为每次我将第一个数字追加到结尾时,如果L = []则[]在这里不起作用。

如果有三个列表,如何停止该功能?

2 个答案:

答案 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中的功能组合。