给定任意切片,例如:
let words = &["one", "one", "one", "two"];
如何知道所有元素是否相同?
更进一步,如果所有元素都相同,如何返回对第一个元素的引用?
本质上,我想写一个这样的函数:
fn are_all_elements_equal<T>(elems: &[T]) -> Option<&T> {
// ... ?
}
我认为这是子切片模式的一个很好的用例:
pub fn are_all_elements_equal<T: PartialEq>(elems: &[T]) -> Option<&T> {
match elems {
[head, tail @ ..] => tail.iter().all(|x| x == head).then(|| head),
[] => None,
}
}
我会使用.all
: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all
首先,如果切片为空,只返回None
。
然后在切片的其余部分抓取一个迭代器,并使用.all
函数检查该元素是否等于您刚刚抓取的第一个元素。如果返回true,则返回Some(first_element)
作为已经发布的答案的扩展,您还可以将其用于任何可以迭代的泛型:
pub fn iter_all_eq<T: PartialEq>(iter: impl IntoIterator<Item = T>) -> Option<T> {
let mut iter = iter.into_iter();
let first = iter.next()?;
iter.all(|elem| elem == first).then(|| first)
}
fn main() {
println!("{:?}", iter_all_eq(&[1, 1, 1]));
println!("{:?}", iter_all_eq(&[1, 2, 1]));
println!("{:?}", iter_all_eq(&["abc", "abc", "abc", "abc"]));
}
游乐场
一种优雅的方法是使用itertools
crate中的tuple_windows
:
use itertools::Itertools;
pub fn are_all_elements_equal<T: Eq>(elems: &[T]) -> Option<&T> {
elems.iter().tuple_windows().all(|(a, b)| a == b).then(|| &elems[0])
}
请注意,这将在空片上出错。为了处理空片,如果elems.is_empty()
,则需要显式地返回None
。
使用可用的内置函数非常简单:
fn check_all<T: Eq>(items: &[T]) -> Option<&T> {
match items.is_empty() {
true => None,
false => items.windows(2).all(|a| a[0] == a[1]).then(|| &items[0])
}
}
操场上联系
.windows(2)
给出一个包含重叠元素对的迭代器。.all(|a| a[0] == a[1])
比较来自每个窗口的两个元素- 如果前一个
.all()
返回true
,则.then(|| &items[0])
返回包含对第一个元素引用的Option
,否则返回None
match items.is_empty()
是必需的,因为如果片为空,.all()
也将返回true
,这将导致items[0]
的恐慌
请注意,由于.all()
中使用的比较可能导致与自身比较的值相同,因此根据此答案,您需要将T
约束为Eq
。