【Rust】がばがばRust独学 - 13. Functional Language Features - 1 Closures (1)
Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)
今回は、Functional Language FeaturesのClosuresについて学びつつ、気になった点をコード確認した結果を記事にしています。
- versions
$ rustc --version rustc 1.40.0 (73528e339 2019-12-16)
- Functional Language Features: Iterators and Closures
- Closures: Anonymous Functions that Can Capture Their Environment
- まとめ
- 最後に
Functional Language Features: Iterators and Closures
Functional Language Features: Iterators and Closures - The Rust Programming Language
一般的な関数型言語の機能に似たRustの機能としては、下記をカバーしています。
- Closures : 変数に格納できる関数のような構造
- Iterators : 一連の要素を処理する方法
また、下記も記載されているそうです。
- Closures 、 Iterators を利用したI/Oの改善方法
- Closures 、 Iterators のパフォーマンス
Closures: Anonymous Functions that Can Capture Their Environment
Rustの Closures は下記が可能な匿名関数です。
- 変数への保存
- 引数として他関数へ渡せる
下記に、公式ドキュメント(Closures: Anonymous Functions that Can Capture Their Environment - The Rust Programming Language)のデモコードを元に、クロージャー利用までの手順を記載致します。
何も考慮せずに書いた場合
simulated_expensive_calculation
関数で約2秒停止するため、 generate_workout
で intensity < 25
の場合、約4秒の待機時間が発生します。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// 実行に約2秒かかる仮想関数 /// fn simulated_expensive_calculation(intensity: u32) -> u32 { println!("calculating slowly..."); let sec: u64 = 2; let duration: Duration = Duration::from_secs(sec); thread::sleep(duration); intensity } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { if intensity < 25 { println!( "Today, do {} pushups!", simulated_expensive_calculation(intensity) ); println!( "Next, do {} situps!", simulated_expensive_calculation(intensity) ); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", simulated_expensive_calculation(intensity) ); } } }
変数の利用
expensive_result
の変数に代入することにより、intensity < 25
の場合でも、約2秒の待機時間になります。
しかし、下記の場合、intensity >= 25 && random_number == 3
の場合であっても待機時間が発生してしまいます。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// 実行に約2秒かかる仮想関数 /// fn simulated_expensive_calculation(intensity: u32) -> u32 { println!("calculating slowly..."); let sec: u64 = 2; let duration: Duration = Duration::from_secs(sec); thread::sleep(duration); intensity } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { let expensive_result = simulated_expensive_calculation(intensity); if intensity < 25 { println!("Today, do {} pushups!", expensive_result); println!("Next, do {} situps!", expensive_result); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!("Today, run for {} minutes!", expensive_result); } } }
Closureの利用
無名関数を expensive_closure
に代入することで、Closureの宣言が可能です。
ただ、下記のままでは、通常で書いた内容と変わりがありません。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { // 実行に約2秒かかる仮想クロージャー let expensive_closure = |num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num }; if intensity < 25 { println!("Today, do {} pushups!", expensive_closure(intensity)); println!("Next, do {} situps!", expensive_closure(intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!("Today, run for {} minutes!", expensive_closure(intensity)); } } }
Closure x Cacherの利用
struct Cacher<T>
としてClosureの結果をキャッシュすることで、無駄な処理がなく、かつ、最大約2秒の処理とすることが可能です。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { // 実行に約2秒かかるキャッシュを利用した仮想クロージャー let mut expensive_result = Cacher::new(|num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num }); if intensity < 25 { println!("Today, do {} pushups!", expensive_result.value(intensity)); println!("Next, do {} situps!", expensive_result.value(intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", expensive_result.value(intensity) ); } } } /// /// キャッシュロジック /// struct Cacher<T> where T: Fn(u32) -> u32, { calculation: T, value: Option<u32>, } /// /// キャッシュロジック impl /// impl<T> Cacher<T> where T: Fn(u32) -> u32, { fn new(calculation: T) -> Cacher<T> { Cacher { calculation, value: None, } } fn value(&mut self, arg: u32) -> u32 { match self.value { Some(v) => v, None => { let v = (self.calculation)(arg); self.value = Some(v); v } } } }
まとめ
let a = |b| { ... }
によってClosureを宣言可能
最後に
Closuresにはまだまだ細かく見るべき点が複数ありますが、一旦今回はここまでにします。
明日、Closures続きを記載します。