我正在阅读这篇文章While or Tail Recursion in F#, what to use when?有几个人说,做事的'功能方式'是通过使用maps/fold和higher order函数来代替递归和循环。在列表中的位置x处返回项目
我有这个函数返回列表中的位置x项目:
let rec getPos l c = if c = 0 then List.head l else getPos (List.tail l) (c - 1)
怎么能转化为更多的功能?
我正在阅读这篇文章While or Tail Recursion in F#, what to use when?有几个人说,做事的'功能方式'是通过使用maps/fold和higher order函数来代替递归和循环。在列表中的位置x处返回项目
我有这个函数返回列表中的位置x项目:
let rec getPos l c = if c = 0 then List.head l else getPos (List.tail l) (c - 1)
怎么能转化为更多的功能?
这是一个原始列表函数(也称为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
,但结果比递归版本不太清楚。
我不确定你的意思是更多的功能。
您是否将此练习作为学习练习?
如果没有,你可能只是试试这个:
> let mylist = [1;2;3;4];;
> let n = 2;;
> mylist.[n];;
你的定义已经相当实用,因为它采用的是尾递归函数,而不是命令式循环结构。但是,它也看起来像Scheme程序员可能写的东西,因为您使用的是head
和tail
。
我怀疑你真的在问如何用更习惯的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."
列表结构的递归现在显示在代码中。如果模式匹配是非详尽的,那么您也会收到警告,因此您不得不处理索引太大的错误。
你说得对,函数式编程还鼓励使用像地图或折叠(所谓的无点式)等组合器。但太多它只会导致无法读取的代码。在这种情况下,我不认为这是有保证的。
当然,Benjol是正确的,在实践中,你只会写mylist.[n]
。
如果您想使用高阶函数对于这一点,你可以这样做:
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个元素显然是您应该使用的基本块。如果您想要Benjol提到的第n个项目,请执行myList.[n]
。
对于构建基本构造,使用递归或可变循环没有任何错误(并且通常,通过以这种方式执行)。
不是作为一个实际的解决方案,但作为一个练习,这里是表达通过foldr
nth
,或在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
不错,但我宁愿做'| h :: [] - > h'表示第一个匹配条件 – 2011-09-14 13:41:11