Rustのクロージャは、変数に保存したり、引数として他の関数に渡すことのできる匿名関数です。ある場所でクロージャを生成し、それから別の文脈でクロージャを呼び出して評価することができます。 関数と異なり、呼び出されたスコープの値をクロージャは、キャプチャすることができます。
アプリユーザの年齢や、 BMI、運動の好み、最近のトレーニング、指定された強弱値などの多くの要因からトレーニングプランを生成するアルゴリズムあるとします。
use std::thread;
use std::time::Duration;
// 重い計算と仮定する
fn simulated_expensive_calculation(intensity: u32) -> u32 {
// ゆっくり計算します
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
intensity
}
fn main() {
// 簡潔にするために一部ハードコード
// ハードコードされたユーザー入力の強弱値
let simulated_user_specified_value = 10;
// ハードコードされたランダム値(rand)
let simulated_random_number = 7;
generate_workout(
simulated_user_specified_value,
simulated_random_number
);
}
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)
);
}
}
}
// 重複削除して一つにする
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
);
}
}
}
let expensive_closure = |num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
};
fn generate_workout(intensity: u32, random_number: u32) {
let expensive_closure = |num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
};
if intensity < 25 {
println!(
"Today, do {} pushups!",
expensive_closure(intensity)//-> 同じクロージャ1
);
println!(
"Next, do {} situps!",
expensive_closure(intensity)//-> 同じクロージャ2
);
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
expensive_closure(intensity)
);
}
}
}
let expensive_closure = |num: u32| -> u32 {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
};
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
let n = example_closure(5);
error[E0308]: mismatched types
--> src/main.rs
|
| let n = example_closure(5);
| ^ expected struct `std::string::String`, found
integral variable
|
= note: expected type `std::string::String`
found type `{integer}`
struct Cacher<T>
where T: Fn(u32) -> u32
{
calculation: T,
value: Option<u32>,
}
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
},
}
}
}
fn generate_workout(intensity: u32, random_number: u32) {
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)
);
}
}
}
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 2);
}
thread 'call_with_different_values' panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `2`', src/main.rs
fn main() {
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));
}
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
for val in v1_iter {
// {}でした
println!("Got: {}", val);
}
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// デフォルト実装のあるメソッドは省略
// methods with default implementations elided
}
[](Iteratorトレイトを実装するには、Item型も定義する必要があり、 そして、このItem型がnextメソッドの戻り値の型に使われている。type, Self::Itemは、関連型についての詳細は、第19章)
#[test]
fn iterator_demonstration() {
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter();
assert_eq!(v1_iter.next(), Some(&1));
assert_eq!(v1_iter.next(), Some(&2));
assert_eq!(v1_iter.next(), Some(&3));
assert_eq!(v1_iter.next(), None);
}
#[test]
fn iterator_sum() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();
assert_eq!(total, 6);
}
let v1: Vec<i32> = vec![1, 2, 3];
v1.iter().map(|x| x + 1);
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(v2, vec![2, 3, 4]);
#[derive(PartialEq, Debug)]
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes.into_iter()
.filter(|s| s.size == shoe_size)
.collect()
}
#[test]
fn filters_by_size() {
let shoes = vec![
Shoe { size: 10, style: String::from("sneaker") },
Shoe { size: 13, style: String::from("sandal") },
Shoe { size: 10, style: String::from("boot") },
];
let in_my_size = shoes_in_my_size(shoes, 10);
assert_eq!(
in_my_size,
vec![
Shoe { size: 10, style: String::from("sneaker") },
Shoe { size: 10, style: String::from("boot") },
]
);
}
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
// イテレータを使用したときに起きてほしいことを書く
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
#[test]
fn using_other_iterator_trait_methods() {
let sum: u32 = Counter::new().zip(Counter::new().skip(1))
.map(|(a, b)| a * b)
.filter(|x| x % 3 == 0)
.sum();
assert_eq!(18, sum);
}
impl Config {
pub fn new(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}
// ここが問題で非効率 マジックナンバーだしね。。。
let query = args[1].clone();
let filename = args[2].clone();
// ここ追加してる?
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
Ok(Config { query, filename, case_sensitive })
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
});
// --snip--
}
↓
fn main() {
let config = Config::new(env::args()).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
});
// --snip--
}
impl Config {
pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
impl Config {
pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
args.next();
let query = match args.next() {
Some(arg) => arg,
// クエリ文字列を取得しませんでした
None => return Err("Didn't get a query string"),
};
let filename = match args.next() {
Some(arg) => arg,
// ファイル名を取得しませんでした
None => return Err("Didn't get a file name"),
};
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
Ok(Config { query, filename, case_sensitive })
}
}
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents.lines()
.filter(|line| line.contains(query))
.collect()
}
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)