Probar si una proyección es uniforme sobre una secuencia F#

CorePress2024-01-25  10

Quiero comprobar si una proyección sobre una secuencia tiene un valor uniforme en F#.

Esto es lo que tengo:

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

Me preguntaba si ya había una función incorporada para esto. Y si no, ¿cuál es la mejor manera de implementarlo?



------------------------------------

Podemos reducir el problema a comparar elementos adyacentes, porque para lograr uniformidad, no podemos tener ningún elemento que no sea igual al anterior.

Esto significa que solo necesitamos verificar si existe uno de esos pares; solo necesitamos enumerar la secuencia hasta que encontremos el par.

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

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

El método base es isUniform, por lo que podemos pasar una secuencia proyectadauencia desde isUniformBy, evitando un paso a través de id. Además, solo utilizamos el espacio O(1).

Pruebas

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)

2

Me gusta este enfoque, pero creo que en realidad quieres que isUniform sea Seq.forall (fun (a, b) -> a = b), o agregar |> no al final de la función.

- Brian Berns

29/03/2021 a las 17:34

@brianberns Me olvidé de eso. Gracias.

-Asti

29/03/2021 a las 18:21



------------------------------------

Puedes combinar las dos primeras llamadas usando Seq.distinctBy (enlace) y luego simplificarlas un poco más mediante Seq.tryExactlyOne (enlace), aunque luego informará falso para una secuencia vacía:

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



------------------------------------

Si esto no se trata de la best (es decir, el más legible y eficiente), pero el camino más corto, seguiría el enfoque de OP y lo optimizaría un poco:

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")

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare