#f#
Вопрос:
Я хочу проверить, имеет ли проекция на последовательность одинаковое значение в 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
Мне было интересно, есть ли уже встроенная функция для этого?
И если нет, то каков наилучший способ реализовать это?
Ответ №1:
Мы можем свести проблему к сравнению соседних элементов — потому что для обеспечения однородности у нас не может быть элемента, который не совпадает с предыдущим.
Это означает, что нам нужно только проверить, есть ли одна такая пара — нам нужно только перечислить последовательность, пока мы не найдем пару.
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) пробел.
Тесты
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)
Комментарии:
1. Мне нравится такой подход, но я думаю , что вы действительно хотите
isUniform
бытьSeq.forall (fun (a, b) -> a = b)
или добавить|> not
в конце функции.2. @brianberns Забыл об этом. Спасибо.
Ответ №2:
Вы можете объединить первые два вызова с помощью Seq.distinctBy
(ссылка), а затем немного упростить с помощью Seq.tryExactlyOne
(ссылка), хотя затем он сообщит false
о пустой последовательности:
let isUniformBy f xs =
xs
|> Seq.distinctBy f
|> Seq.tryExactlyOne
|> Option.isSome
Ответ №3:
Если речь идет не о лучшем (то есть наиболее удобочитаемом и эффективном), а о кратчайшем пути, я бы последовал подходу 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")