List 如何使这些简单函数在f中进行尾部递归#

List 如何使这些简单函数在f中进行尾部递归#,list,recursion,f#,tail-recursion,List,Recursion,F#,Tail Recursion,我有这两个功能 //Remove all even indexed elements from a list and return the rest let rec removeEven l = match l with | x0::x1::xs -> x1::removeEven (xs) | [] -> [] | [_] -> [] //combine list members into pairs let rec combinePair l = match l with

我有这两个功能

//Remove all even indexed elements from a list and return the rest
let rec removeEven l =
match l with
| x0::x1::xs -> x1::removeEven (xs)
| [] -> []
| [_] -> []

//combine list members into pairs
let rec combinePair l =
match l with
| x0::x1::xs -> (x0,x1) :: combinePair(xs)
| [] -> []
| [_] -> []
那工作

但是我想现在我已经做到了,我可能会学习一些关于尾部递归的知识,这是我很难掌握的

这就是为什么我认为,如果我能得到一些帮助,让我自己制作的函数成为递归的尾部,也许它的工作原理会变得更加清晰,而不是像我自己的代码那样阅读我可能不理解的示例(请记住,我是一个完全的f#新手:)


当然,欢迎对我的代码提出任何其他建设性意见

使函数在F#中尾部递归的一种典型方法是使用一个列表(
acc
,在本例中)来累积结果,并将其反转以获得正确的顺序:

let removeEven l =
    let rec loop xs acc =
        match xs with        
        | [] | [_] -> acc
        | _::x1::xs' -> loop xs' (x1::acc)
    loop l [] |> List.rev

let combinePair l =
    let rec loop xs acc =
        match xs with        
        | [] | [_] -> acc
        | x0::x1::xs' -> loop xs' ((x0, x1)::acc)
    loop l [] |> List.rev
因为我们只是在每次递归调用
循环
后返回结果,所以这些函数是尾部递归的

您的函数看起来很不错,但我仍有几点意见:

  • 缩进在F#中很重要。我更喜欢
    匹配。。。with
    lec rec
    声明后面的几个空格
  • 模式匹配案例应遵循一致的顺序。首先从基本案例开始是个好主意
  • 每当你有一个
    funt t->match t with
    的模式时,
    function
    关键字自然会用于缩短函数
  • 最好去掉不必要的括号,尤其是在只有一个参数的函数中
应用上述注释,您的功能如下所示:

// Remove all even indexed elements from a list and return the rest
let rec removeEven = function
    | [] | [_] -> []
    | _::x1::xs -> x1::removeEven xs

// Combine list members into pairs
let rec combinePair = function
    | [] | [_] -> []
    | x0::x1::xs -> (x0, x1)::combinePair xs

如果您需要一种更慢、更不易维护、占用更多内存的方法,可以使用延续

let removeEven items = 
  let rec loop f = function
    | _::h::t -> loop (fun acc -> f (h::acc)) t
    | [] | [_] -> f []
  loop id items

但是,嘿,它是尾部递归的。

真不错!thx很多这正是我想要的。不完全确定我是否理解“loop l[]|>List.rev”行的功能。我的意思是,我知道它应该反转列表,但为什么循环l[]?什么时候才能到达rec代码?
[]->[].[].[].[].[].[].[/code>也可以折叠成
[].[].[].[].[/code>,保存两行。@ildjarn:您的建议已被应用:)。@PNS:该行与
List.rev(循环l[])相同。
。我们从一个空的列表开始积累。当输入列表少于2个元素时,我们返回累加器列表并将其反转。我认为这是正确的。它应该删除偶数索引元素,而不是偶数值元素,在这种情况下,1具有索引0,2具有索引1为什么
removeEven[1;2]
返回
[2]
?我在回答中复制了它的行为,但它似乎应该被称为
returnEven
removeOdd
或其他什么。很抱歉删除我的评论。我重新措辞了。那么,偶数指的是指数?好的。@Daniel:代码顶部的注释一直这么说。.-]@伊尔贾恩:谁读代码注释?!?:-)我认为结果应该是肯定的reversed@TysonWilliams不,先生。没有。哦,是的。你的回答是正确的。对不起,我弄错了。(我知道我应该在发表评论之前测试它:P)