FSharp.Core 中的 `id` 函数有什么用?

What's the purpose of `id` function in the FSharp.Core?

来自Operators.id<'T> Function (F#)

The identity function.

Parameters: x Type: 'T (The input value)

Return Value: The same value

F# Core Library Versions, supported in: 2.0, 4.0, Portable

为什么有一个函数 returns 它的输入?

它对某些高阶函数(将函数作为参数的函数)很有用,因此您可以将 id 作为参数传递,而不是写出 lambda (fun x -> x).

[[1;2]; [3]] |> List.collect id  // [1; 2; 3]

使用高阶函数(即 return 其他函数 and/or 将其他函数作为参数的函数)时,您始终必须提供 something 作为参数,但并不总是有您想要应用的实际数据转换。

例如,函数Seq.collect展平了一个序列的序列,并采用一个函数return将"nested"序列作为"outer"序列的每个元素.例如,这就是您可能获得某种 UI 控件的所有孙子列表的方式:

let control = ...
let allGrandChildren = control.Children |> Seq.collect (fun c -> c.Children)

但很多时候,序列中的每个元素本身就是一个序列 - 例如,您可能有一个列表列表:

let l = [ [1;2]; [3;4]; [5;6] ]

在这种情况下,您传递给 Seq.collect 的参数函数只需要 return 参数:

let flattened = [ [1;2]; [3;4]; [5;6] ] |> Seq.collect (fun x -> x)

这个表达式 fun x -> x 是一个函数,它只是 return 它的参数,也称为“恒等函数”。

let flattened = [ [1;2]; [3;4]; [5;6] ] |> Seq.collect id

在使用高阶函数(例如上面的 Seq.collect)时,它的用法如此频繁,因此它应该在标准库中占有一席之地。

另一个引人注目的示例是 Seq.choose - 一个过滤一系列 Option 值并同时展开它们的函数。例如,这就是您可以将所有字符串解析为数字并丢弃无法解析的字符串的方式:

let tryParse s = match System.Int32.TryParse s with | true, x -> Some x | _ -> None
let strings = [ "1"; "2"; "foo"; "42" ]
let numbers = strings |> Seq.choose tryParse  // numbers = [1;2;42]

但是,如果您已经获得了一个包含 Option 个值的列表怎么办?身份函数来拯救!

let toNumbers optionNumbers =
   optionNumbers |> Seq.choose id

它在处理选项时非常有用。

我写了一个小的惯用的 JSON-Helper,将所有可选字段指示为 Option,抛出错误,如果字符串作为 null 传递,如果不是 'string option' 类型。

现在有一个函数提供了一个装箱的输出值,可以是

  1. 'a -> 任何类型但没有选项
  2. 'b -> 'x 选项

为了正确装箱值,我使用

val |> if isOption then fnOptTransform else id

所以我正在应用高阶函数 fnOptTransform,并通过调用 id 否则,避免编写单独的 lambda 的丑陋(我尽量避免它,我可以......)。觉得有用。