列表理解与F#中的高阶函数

pad*_*pad 8 f# list-comprehension list

我来自SML背景,对高阶函数感觉很舒服.但我真的不明白列表理解.是否存在列表理解比高阶函数更合适的情况,List反之亦然?

我听说列表理解比高阶函数慢,我应该避免在编写性能关键函数时使用它吗?

为了示例,请查看在F#有效地投影列表列表,其中@cfern的答案分别包含使用列表推导和高阶函数的两个版本:

let rec cartesian = function
  | [] -> [[]]
  | L::Ls -> [for C in cartesian Ls do yield! [for x in L do yield x::C]]
Run Code Online (Sandbox Code Playgroud)

和:

let rec cartesian2 = function
  | [] -> [[]]
  | L::Ls -> cartesian2 Ls |> List.collect (fun C -> L |> List.map (fun x->x::C))
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 11

在理解和高阶函数之间进行选择主要是风格问题.我认为理解有时更具可读性,但这只是个人偏好.请注意,该cartesian函数可以更优雅地编写如下:

let rec cartesian = function  
  | [] -> [[]]  
  | L::Ls -> 
     [ for C in cartesian Ls do for x in L do yield x::C ]
Run Code Online (Sandbox Code Playgroud)

有趣的情况是编写递归函数.如果使用序列(和序列推导),它们会删除一些不必要的临时列表分配,如果yield!在尾部调用位置使用,还可以避免堆栈溢出异常:

let rec nums n = 
  if n = 100000 then []
  else n::(nums (n+1))
// throws StackOverflowException
nums 0 

let rec nums n = seq {
  if n < 100000 then
    yield n
    yield! nums (n+1) }
// works just fine
nums 0 |> List.ofSeq 
Run Code Online (Sandbox Code Playgroud)

这是一个非常有趣的模式,因为它不能使用列表以相同的方式编写.使用列表时,您不能返回某个元素然后进行递归调用,因为它对应于n::(nums ...),而不是尾递归.