Open10
「なっとく!関数型プログラミング」をRustで
第2章
struct ShoppingCart {
items: Vec<String>,
book_added: bool,
}
impl ShoppingCart {
fn new() -> Self {
Self {
items: vec![],
book_added: false,
}
}
fn add_item(&mut self, item: &str) {
self.items.push(item.to_owned());
if item == "Book" {
self.book_added = true;
}
}
fn get_items(&self) -> Vec<String> {
self.items.clone()
}
}
removeを追加
struct ShoppingCart {
items: Vec<String>,
book_added: bool,
}
impl ShoppingCart {
fn new() -> Self {
Self {
items: vec![],
book_added: false,
}
}
fn add_item(&mut self, item: &str) {
self.items.push(item.to_owned());
if item == "Book" {
self.book_added = true;
}
}
fn get_items(&self) -> Vec<String> {
self.items.clone()
}
fn remove_item(&mut self, item: &str) {
if let Some(pos) = self.items.iter().position(|i| i == item) {
self.items.remove(pos);
if item == "Book" {
self.book_added = false;
}
};
}
}
// 上の書き方だと、本を2冊追加してから1冊削除した場合、本はあるのに
// book_addedがfalseになってしまう
fn main() {
let mut cart = ShoppingCart::new();
cart.add_item("Book");
cart.add_item("Book");
cart.remove_item("Book");
dbg!(&cart.items);
}
book_addedを消して、都度判定するように変更
struct ShoppingCart {
items: Vec<String>,
}
impl ShoppingCart {
fn new() -> Self {
Self {
items: vec![],
}
}
fn add_item(&mut self, item: &str) {
self.items.push(item.to_owned());
}
fn get_discount_percentage(&self) -> usize {
if self.items.contains(&"Book".to_string()) {
return 5;
} else {
return 0;
}
}
fn get_items(&self) -> Vec<String> {
self.items.clone()
}
fn remove_item(&mut self, item: &str) {
if let Some(pos) = self.items.iter().position(|i| i == item) {
self.items.remove(pos);
};
}
}
itemsを外から渡すようにして純粋な関数に
struct ShoppingCart;
impl ShoppingCart {
fn get_discount_percentage(items: Vec<String>) -> usize {
if items.contains(&"Book".to_string()) {
return 5;
} else {
return 0;
}
}
}
fn main() {
let mut items = vec![];
items.push("Apple".to_string());
dbg!("{}", ShoppingCart::get_discount_percentage(items));
}
第3章
最初の良くないコードはRustでの再現方法が分からなかったので修正後のみ
fn replan(plan: &Vec<String>, new_city: &str, before_city: &str) -> Vec<String> {
let mut new_plan = plan.clone();
if let Some(new_city_index) = plan.iter().position(|c| c == before_city) {
new_plan.insert(new_city_index, new_city.to_owned());
};
new_plan
}
fn main() {
let mut plan_a = vec![];
plan_a.push("Paris".to_string());
plan_a.push("Berlin".to_string());
plan_a.push("Krakow".to_string());
let plan_b = replan(&plan_a, "Vienna", "Krakow");
dbg!(&plan_a);
dbg!(&plan_b);
}
第4章
use std::cmp::Ordering;
/// 単語のスコアは'a'以外の文字ごとに1ポイント
/// 単語のリストが与えられたらスコアの高い順に並べ替えたリストを返す
fn score(word: &str) -> usize {
word.replace("a", "").len()
}
fn score_comparator(w1: &str, w2: &str) -> Ordering {
score(w2).cmp(&score(w1))
}
fn ranked_words<'a>(words: &'a [&'a str]) -> Vec<&'a str> {
let mut new_words = words.clone().to_owned();
new_words.sort_by(|a, b| score_comparator(a, b));
new_words
}
fn main() {
let words = vec!["ada", "haskell", "scala", "java", "rust"];
let ranking = ranked_words(&words);
dbg!(&ranking);
}
比較する関数を引数で渡すように変更
use std::cmp::Ordering;
/// 単語のスコアは'a'以外の文字ごとに1ポイント
/// 単語のリストが与えられたらスコアの高い順に並べ替えたリストを返す
fn score(word: &str) -> usize {
word.replace("a", "").len()
}
fn score_comparator(w1: &str, w2: &str) -> Ordering {
score(w2).cmp(&score(w1))
}
fn ranked_words<'a>(comparator: fn(&str, &str) -> Ordering, words: &'a [&'a str]) -> Vec<&'a str> {
let mut new_words = words.clone().to_owned();
new_words.sort_by(|a, b| comparator(a, b));
new_words
}
fn main() {
let words = vec!["ada", "haskell", "scala", "java", "rust"];
let ranking = ranked_words(score_comparator, &words);
dbg!(&ranking);
}
外部から渡すように変更したことでスコア計算に変更が出ても対応できるようになった
use std::cmp::Ordering;
/// 単語のスコアは'a'以外の文字ごとに1ポイント
/// 単語のリストが与えられたらスコアの高い順に並べ替えたリストを返す
/// 単語に'c'が含まれている場合は5ポイントのボーナススコアを加算する
fn score(word: &str) -> usize {
word.replace("a", "").len()
}
fn score_with_bonus(word: &str) -> usize {
let base = score(word);
if word.contains("c") {
return base + 5;
} else {
return base;
}
}
fn score_comparator(w1: &str, w2: &str) -> Ordering {
score(w2).cmp(&score(w1))
}
fn score_with_bonus_comparator(w1: &str, w2: &str) -> Ordering {
score_with_bonus(w2).cmp(&score_with_bonus(w1))
}
fn ranked_words<'a>(comparator: fn(&str, &str) -> Ordering, words: &'a [&'a str]) -> Vec<&'a str> {
let mut new_words = words.clone().to_owned();
new_words.sort_by(|a, b| comparator(a, b));
new_words
}
fn main() {
let words = vec!["ada", "haskell", "scala", "java", "rust"];
let ranking = ranked_words(score_with_bonus_comparator, &words);
dbg!(&ranking);
}
ただ、似たような処理が出てしまう
ボーナスは加点される値だけ求める関数に変更し、最終的なスコアを求める関数はclosureで定義するように変更。これによりペナルティのような要件が追加されても対応しやすくなる
fn score(word: &str) -> isize {
word.replace("a", "").len().try_into().unwrap()
}
fn bonus(word: &str) -> isize {
if word.contains("c") {
return 5;
} else {
return 0;
}
}
fn penalty(word: &str) -> isize {
if word.contains("s") {
return 7;
} else {
0
}
}
fn ranked_words<'a>(word_score: fn(&str) -> isize, words: &'a [&'a str]) -> Vec<&'a str> {
let mut new_words = words.clone().to_owned();
new_words.sort_by(|a, b| word_score(b).cmp(&word_score(a)));
new_words
}
fn main() {
let words = vec!["ada", "haskell", "scala", "java", "rust"];
let score_func: fn(&str) -> isize = |x| {(score(x) + bonus(x) - penalty(x)).try_into().unwrap()};
let ranking = ranked_words(score_func, &words);
dbg!(&ranking);
}
与えられたリストに対して、各要素をスコアに変換したリストを返す関数
fn word_scores<'a>(word_score: fn(&str) -> isize, words: &'a [&'a str]) -> Vec<isize> {
words.iter().map(|w| word_score(w)).collect()
}