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