11月 19, 2023 LaGee
之前有提到take [1]
這篇提他的好兄弟take_while
take是回傳前幾個元素
而take_while則是回傳元素直到不滿足條件
take_while
take_while被歸類在iter下面
文件[2]這樣解釋
創建一個迭代器,取出滿足條件的元素
fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where
Self: Sized,
P: FnMut(&Self::Item) -> bool,
self為迭代器自身
predicata為一個閉包
會搜索元素,直到第一個false為止
最後回傳TakeWhile迭代器
例子
假設我們要找出一個數組中
小於等於五的數字
可以這樣打
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result: Vec<_> = numbers.into_iter().take_while(|&x| x <= 5).collect();
println!("{:?}", result);
[1, 2, 3, 4, 5]
將數組透過into_iter放入迭代器
使用take_while
找到第一個小於等於5的
也就是會把6以前的都輸出
那這樣會有個疑問
他個filter有什麼差
filter這樣做也可以達成小於等於五
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result: Vec<_> = numbers.into_iter().filter(|&x| x <= 5).collect();
println!("{:?}", result);
[1, 2, 3, 4, 5]
差別在take_while會在false的第一時間就結束迭代
因此如果數組變成
let numbers = vec![1, 2, 3, 4, 5, 6, 7, -8, 9, 10];
let result: Vec<_> = numbers.into_iter().filter(|&x| x <= 5).collect();
println!("{:?}", result);
let numbers = vec![1, 2, 3, 4, 5, 6, 7, -8, 9, 10];
let result: Vec<_> = numbers.into_iter().take_while(|&x| x <= 5).collect();
println!("{:?}", result);
[1, 2, 3, 4, 5, -8]
[1, 2, 3, 4, 5]
當迭代到-6時
take_while會停止
filter則是會繼續濾
實際應用
最近做題目,CodeWars[3]有一題蠻有趣的
A beer can pyramid will square the number of cans in each level - 1 can in the top level, 4 in the second, 9 in the next, 16, 25...Complete the beeramid function to return the number of complete levels of a beer can pyramid you can make, given the parameters of:
your referral bonus, and
the price of a beer can
有一個啤酒塔,算出可以疊幾層
最上面是1個
第二層會是4個
第三層會是9個
以此類推,下面那層會是層數的平方
簡單畫一下3D圖,也就是長這樣
會給予兩個值
一個是總資產,另一個是一個啤酒是多少錢
透過這資產和單價算出可以疊幾層
fn beeramid (bonus: i32, price: f32) -> usize {
}
可以用下面這些測試
#[cfg(test)]
mod tests {
use super::beeramid;
#[test]
fn sample_tests() {
assert_eq!(beeramid(9, 2.0), 1);
assert_eq!(beeramid(10, 2.0), 2);
assert_eq!(beeramid(11, 2.0), 2);
assert_eq!(beeramid(21, 1.5), 3);
assert_eq!(beeramid(454, 5.0), 5);
assert_eq!(beeramid(455, 5.0), 6);
assert_eq!(beeramid(4, 4.0), 1);
assert_eq!(beeramid(3, 4.0), 0);
assert_eq!(beeramid(0, 4.0), 0);
assert_eq!(beeramid(-1, 4.0), 0);
}
}
解題
這題就很適合用take_while
let tmp = ((bonus as f32) / price) as i32;
(1..=tmp)
.into_iter()
.scan(0, |state, x| {
*state = *state + x * x;
Some(*state)
})
.take_while(|&x| x <= tmp)
.count()
bouns為總資產,price為每一瓶價格
因此tmp為可以買幾瓶
使用scan[4]跟take_while配合可以達成題目要求
scan可以創建一個的迭代器,內容物為疊加平方值,其數值代表這一層往上共有幾個瓶子
透過take_while,抓出疊起來的瓶子小於等於買的數量
然後透過count計算總迭代次數,也就是層數
參考資料
[1] https://lageeblog.blogspot.com/2023/11/rust-take.html
[2] https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take_while
[3] https://www.codewars.com/kata/51e04f6b544cf3f6550000c1
[4] https://lageeblog.blogspot.com/2023/11/rust-scan.htm
0 comments:
張貼留言