How can I implement a tail-recursive list append?

后端 未结 3 765
无人及你
无人及你 2020-12-06 02:52

A simple append function like this (in F#):

let rec app s t =
   match s with
      | [] -> t
      | (x::ss) -> x :: (app ss t)

will

相关标签:
3条回答
  • 2020-12-06 03:04

    From a quick glance at the F# sources, it seems the tail is internally mutable. A simple solution would be to reverse the first list before consing its elements to the second list. That, along with reversing the list, are trivial to implement tail recursively.

    0 讨论(0)
  • 2020-12-06 03:07

    Traditional (not tail-recursive)

    let rec append a b =
        match a, b with
        | [], ys -> ys
        | x::xs, ys -> x::append xs ys
    

    With an accumulator (tail-recursive)

    let append2 a b =
        let rec loop acc = function
            | [] -> acc
            | x::xs -> loop (x::acc) xs
        loop b (List.rev a)
    

    With continuations (tail-recursive)

    let append3 a b =
        let rec append = function
            | cont, [], ys -> cont ys
            | cont, x::xs, ys -> append ((fun acc -> cont (x::acc)), xs, ys)
        append(id, a, b)
    

    Its pretty straight-forward to convert any non-tail recursive function to recursive with continuations, but I personally prefer accumulators for straight-forward readability.

    0 讨论(0)
  • 2020-12-06 03:10

    In addition to what Juliet posted:

    Using sequence expressions
    Internally, sequence expressions generate tail-recursive code, so this works just fine.

    let append xs ys = 
      [ yield! xs
        yield! ys ]
    

    Using mutable .NET types
    David mentioned that F# lists can be mutated - that's however limited only to F# core libraries (and the feature cannot be used by users, because it breaks the functional concepts). You can use mutable .NET data types to implement a mutation-based version:

    let append (xs:'a[]) (ys:'a[]) = 
      let ra = new ResizeArray<_>(xs)
      for y in ys do ra.Add(y)
      ra |> List.ofSeq
    

    This may be useful in some scenarios, but I'd generally avoid mutation in F# code.

    0 讨论(0)
提交回复
热议问题