测试投影在 F# 序列上是否均匀

Testing if a projection is uniform over an F# sequence

我想检查序列上的投影在 F# 中是否具有统一值。

这是我的:

module Seq = 

  let isUniformBy (f) (xs : seq<_>) = 
    let l = 
      xs
      |> Seq.map f
      |> Seq.distinct
      |> Seq.truncate 2
      |> Seq.length
    
    l < 2

  let isUniform xs = isUniformBy id xs

printfn "%b" <| Seq.isUniformBy id [ 1; 2; 3 ] // false
printfn "%b" <| Seq.isUniformBy id [ 1; 1; 1 ] // true
printfn "%b" <| Seq.isUniformBy id [ ] // true
printfn "%b" <| Seq.isUniformBy id [ 1; 1 ] // true
printfn "%b" <| Seq.isUniformBy id [ 1; 1; 2 ] // false
printfn "%b" <| Seq.isUniformBy id [ 1; 2 ] // false
printfn "%b" <| Seq.isUniformBy (fun x -> x % 2) [ 2; 4; 6; 8 ] // true

我想知道是否已经有内置函数? 如果没有,实现它的最佳方法是什么?

您可以使用 Seq.distinctBy (link), and then simplify slightly more via Seq.tryExactlyOne (link) 组合前两个调用,尽管它随后会报告 false 为空序列:

let isUniformBy f xs =
    xs
        |> Seq.distinctBy f
        |> Seq.tryExactlyOne
        |> Option.isSome

我们可以将问题简化为比较相邻元素 - 因为为了统一起见,我们不能有任何与前面的元素不同的元素。

这意味着我们只需要检查是否存在这样的一对——我们只需要枚举序列直到找到一对。

let isUniform xs = 
    xs
    |> Seq.pairwise
    |> Seq.exists (fun (a, b) -> a <> b)
    |> not

let isUniformBy (f) (lst : seq<_>) = 
    lst |> Seq.map f |> isUniform

基本方法是 isUniform,因此我们可以将投影序列从 isUniformBy 传递给它,避免通过 id。此外,我们只使用 O(1) space.

测试

assert( Seq.isUniformBy id [ 1; 2; 3 ] = false)
assert( Seq.isUniformBy id [ 1; 1; 1 ] = true)
assert( Seq.isUniformBy id [ ] = true)
assert( Seq.isUniformBy id [ 1; 1 ] = true)
assert( Seq.isUniformBy id [ 1; 1; 2 ] = false)
assert( Seq.isUniformBy id [ 1; 2 ] = false)
assert( Seq.isUniformBy (fun x -> x % 2) [ 2; 4; 6; 8 ] = true)

如果这不是最好的(即最易读、最高效),而是最短的方法,我会按照 OP 的方法稍微优化一下:

module Seq =
    let isUniformBy f x = Seq.groupBy f x |> Seq.length < 2
    // val isUniformBy : f:('a -> 'b) -> x:seq<'a> -> bool when 'b : equality

[ id, [ 1; 2; 3 ] // false
  id, [ 1; 1; 1 ] // true
  id, [ ]         // true
  id, [ 1; 1 ]    // true
  id, [ 1; 1; 2 ] // false
  id, [ 1; 2 ]    // false
  (fun x -> x % 2), [ 2; 4; 6; 8 ]] // true
|> Seq.iter ((<||) Seq.isUniformBy >> printfn "%b")