Skip to content

zongwu's blog

Rust Chars

练习题

codewars上有一道题:

This time no story, no theory.

The examples below show you how to write function accum:

Examples:

accum("abcd") -> "A-Bb-Ccc-Dddd"
accum("RqaEzty") -> "R-Qq-Aaa-Eeee-Zzzzz-Tttttt-Yyyyyyy"
accum("cwAt") -> "C-Ww-Aaa-Tttt"

The parameter of accum is a string which includes only letters from a..z and A..Z.

要求实现accum()

题目本身不难,大体思路是:对于入参,转变成字符列表,然后对每个字符进行repeat m 次,m就等于该字符在字符串的index,(repeat 结果的首字母大写),最后使用-将这些结果join起来就完成了。

对于str有个chars()方法,文档说明:

pub fn chars(&self) -> Chars<'_>

例如:

let word = "goodbye";
let mut chars = word.chars();
assert_eq!(Some('g'), chars.next());
assert_eq!(Some('o'), chars.next());
assert_eq!(Some('o'), chars.next());
assert_eq!(Some('d'), chars.next());
assert_eq!(Some('b'), chars.next());
assert_eq!(Some('y'), chars.next());
assert_eq!(Some('e'), chars.next());

assert_eq!(None, chars.next());

返回的Chars类型,是str的在其chars的迭代器。

接下来我们会对每个字符做map操作,但是我们同时需要每个字符在字符串的index位置。这时候就要介绍一下enumerate()方法。在迭代数据的时候,同时给出当前的索引值。

fn enumerate(self) -> Enumerate<Self>

例如:

let a = ['a', 'b', 'c'];

let mut iter = a.iter().enumerate();

assert_eq!(iter.next(), Some((0, &'a')));
assert_eq!(iter.next(), Some((1, &'b')));

如何重复某个字符m次?标准库里有个repeat()方法:

let a= "a";
let b= std::iter::repeat(a).take(3).collect::<String>();
println!("result {}",b);

关于join()方法,先看看其在slice的定义:

pub fn join<Separator>(
    &self,
    sep: Separator
) -> <[T] as Join<Separator>>::Output
where
    [T]: Join<Separator>, 

slice T 转变成使用Separator连接的Output类型的值。

Join是个trait 它的定义是

pub trait Join<Separator> {
    type Output;
    fn join(slice: &Self, sep: Separator) -> Self::Output;
}

其中定义了一个 Associated Type OutputJoin trait自带了几个实现:

//使用 &str join [S],这个最常用
impl<'_, S> Join<&'_ str> for [S]
where
    S: Borrow<str>,

例如:

assert_eq!(["hello", "world"].join(" "), "hello world");

//使用 &T join [V]
impl<'_, T, V> Join<&'_ T> for [V]
where
    T: Clone,
    V: Borrow<[T]>, 

例如:

//这里 T 与 V 是同一个type了
assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]);

//使用 &[T] join [V]
impl<'_, T, V> Join<&'_ [T]> for [V]
where
    T: Clone,
    V: Borrow<[T]>, 

例如:

assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]);

讲完slice再讲讲std::vev::Vecjoin()方法。

嗯,就比较简单了,std::vev::Vec凭借着 Methods from Deref<Target = [T]> Vec直接就具备了slice的能力。

好了吧上面的步骤组合起来的到解决方案:

fn accum(s:&str)->String {
    s
    .to_lowercase()
    .chars()
    .enumerate()
    .map(|(i,e)| e.to_uppercase().to_string() + 
            &std::iter::repeat(e).take(i).collect::<String>() ) 
    .collect::<Vec<_>>()
    .join("-")
}

需要稍微解释的是我们将大写字母拼接上repeat() 了 m-1(刚好是i)次的字符。而不是repeat() m次。

类图

于是趁着做这道题,就梳理一下str,slice,String等等类型之间的关系。

rust-string-relations