返回列表中位置x的项目

时间:2011-04-16 21:09:08

标签: f# tail-recursion

我正在阅读这篇文章While or Tail Recursion in F#, what to use when?有几个人说,做事的“功能方式”是使用地图/折叠和更高阶函数而不是递归和循环。

我有这个函数返回列表中位置x的项目:

let rec getPos l c =  if c = 0 then List.head l else getPos (List.tail l) (c - 1)

如何将其转换为更具功能性?

5 个答案:

答案 0 :(得分:5)

这是一个基本列表函数(也称为List.nth)。

使用递归是可以的,尤其是在创建基本构建块时。虽然使用模式匹配而不是if-else会更好,例如:

let rec getPos l c =
   match l with
   | h::_ when c = 0 -> h
   | _::t -> getPos t (c-1)
   | [] -> failwith "list too short"

可以用List.fold表达此函数,但结果不如递归版本清晰。

答案 1 :(得分:1)

我不确定你的意思是什么更具功能性。

你是自己动手做这个学习练习吗?

如果没有,你可以试试这个:

> let mylist = [1;2;3;4];;
> let n = 2;;
> mylist.[n];;

答案 2 :(得分:1)

您的定义已经非常实用,因为它使用尾递归函数而不是命令式循环结构。但是,它看起来像一个Scheme程序员可能写的东西,因为你正在使用headtail

我怀疑你真的在问一个更惯用的ML风格。答案是使用模式匹配:

let rec getPos list n =
    match list with
    | hd::tl ->
        if n = 0 then hd
        else getPos tl (n - 1)
    | [] -> failWith "Index out of range."

现在,代码中显示了列表结构的递归。如果模式匹配非穷举,您也会收到警告,因此您不得不处理索引太大的错误。

你是对的,函数式编程也鼓励使用像map或fold这样的组合器(所谓的无点样式)。但过多的只会导致无法读取的代码。在这种情况下,我认为这是不合理的。

当然,Benjol是对的,实际上你只需要写mylist.[n]

答案 3 :(得分:1)

如果你想为此使用高阶函数,你可以这样做:

let nth n = Seq.take (n+1) >> Seq.fold (fun _ x -> Some x) None

let nth n = Seq.take (n+1) >> Seq.reduce (fun _ x -> x)

但这个想法实际上是要有基本的结构,并将它们组合起来构建你想要的任何东西。获取序列的第n个元素显然是您应该使用的基本块。如果你想要第n个项目,就像Benjol所提到的那样,做myList.[n]

对于构建基本结构,使用递归或可变循环没有错(通常,你这样做)。

答案 4 :(得分:0)

不是一个实际的解决方案,但作为练习,以下是通过nth表达foldr的方法之一,或者用F#术语表达List.foldBack

let myNth n xs =
    let step e f = function |0 -> e |n -> f (n-1)
    let error _ = failwith "List is too short"
    List.foldBack step xs error n