F#-在排序列表中插入元素(尾部递归)

F#-在排序列表中插入元素(尾部递归),f#,tail-recursion,F#,Tail Recursion,我试图将下面的普通递归代码转换为F#中的尾部递归,但我失败得很惨 let rec insert elem lst = match lst with | [] -> [elem] | hd::tl -> if hd > elem then elem::lst else hd::(insert elem tl) let lst1 = []

我试图将下面的普通递归代码转换为F#中的尾部递归,但我失败得很惨

let rec insert elem lst = 
    match lst with
    | [] -> [elem]
    | hd::tl -> if hd > elem then 
                    elem::lst
                else 
                    hd::(insert elem tl) 

let lst1 = []

let lst2 = [1;2;3;5]

printfn "\nInserting 4 in an empty list:  %A" (insert 4 lst1)
printfn "\nInserting 4 in a sorted list:  %A" (insert 4 lst2)

你们能帮忙吗?不幸的是,我是f#的初学者。另外,有谁能给我介绍一个好的教程来理解尾部递归吗?

尾部递归的要点如下:从函数返回之前的最后一个操作是对自身的调用;这称为尾部调用,尾部递归就是从中获得名称的(递归调用位于最后,即尾部位置)

您的函数不是尾部递归的,因为它的至少一个分支在递归调用之后有一个操作(list cons操作符)

将递归函数转换为尾部递归函数的常用方法是添加一个参数来累加中间结果(累加器)。当涉及到列表时,当您意识到唯一的基本列表操作是在元素前加前缀时,这也意味着在您处理完列表后,它将被反转,因此通常必须再次反转生成的累加器

考虑到所有这些要点,并且考虑到我们不想通过添加从调用方的角度看是多余的参数来更改函数的公共接口,我们将实际工作转移到内部子函数。这个特定的函数稍微复杂一些,因为在插入元素之后,除了再次连接两个部分列表之外,没有其他事情可做,其中一个现在是按相反顺序排列的,而另一个则不是。我们创建了第二个内部函数来处理该部分,因此整个函数如下所示:

let insert elm lst =
    let rec iter acc = function
        | [] -> List.rev (elm :: acc)
        | (h :: t) as ls ->
            if h > elm then finish (elm :: ls) acc
            else iter (h :: acc) t
    and finish acc = function
        | [] -> acc
        | h :: t -> finish (h :: acc) t
    iter [] lst

为了进一步学习,Scott Wlaschin的F#For Fun and Profile是一个很好的资源,关于递归类型和更多的内容在更大的一章中介绍了尾部递归:

它确实工作得很好……但我想完全跳过结束。让rec finish acc=function |[]->acc | hd::tail->finish(hd::acc)tail let insert elem lst=let rec helper acc=function |[]->List.rev(elem::acc)| hd::tail->if hd>elem然后完成(elem::hd::tail)acc else helper(hd::acc)tail helper[]lst