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
Output
。 Join 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::Vec
的 join()
方法。
嗯,就比較簡單了, 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
等等型別之間的關係。