exercism 是一个不错的题目网站,但是它的过程并不是很方便,需要下载,构建测试才能知道,你的对错。所以有没有方便点的方式呢?
网页练习功能
这项功能是借助mdBook工具完成的,在 mdBook 生成的静态页面中,Rust 代码可以直接执行,它的工作原理是 js 底层与 Rust 游乐场 API 的交互。但是,原有的实现并不是 Rust 测试模式,所以我们要改些事情。(请往下阅读)
TODO
我们要改造的关键:
theme/book.js
改造- [x] 使用
cargo test
- [x] 合并用户输入与测试用例
- [x] 使用
修改默认主题的book.js
,如果你对这个修改过程感兴趣,请查阅使用 Cargo test
- [x] 自动化测试静态页面的代码执行。
为了对改造代码的有效性,有一定了解,我选择对页面进行自动化测试,我把它放在了github 上,不过我不建议你自行运行,因为会耗费时间与计算机资源,甚至无法完整通过测试(需要浏览器,打开 88 个网页)。其中借助 webdriver 测试项目,如有相关需求,可以自行了解,它的 API 还是很好用的。
说明
每个练习网页主要分为四个部分
-
- 说明/题目
-
- 未完成的代码(可编辑,直接运行就好)
-
- 测试代码(不可编辑)
-
- 示例答案(不可编辑,直接运行就好)
中文翻译列表
每个练习,最初来自英文人群,所以可能在某些说明上,和我国国情并不吻合,如遇到这种情况,完全可以 Issue/PR 该 github 项目,改成符合国情的问题。
测试代码与未完成代码之间,会有重叠的库导入(因我会把他俩合并,扔给 Rust 游乐场),所以需要修正。
还有,每个练习本身,都是完整的 Cargo 项目。其中自然少不了对其他非内置箱子的导入,在这时,我能做的,只是提示你们,某某项目无法通过网页测试(因,Rust 游乐场并没有该箱子):
- [x] 为中文校对,但有时出题的人,就只是搬维基百科的资料,所以,题目描述可能不是说百分百贴切。
>
为测试代码修正。no
具有非内置箱子,无法编译成功, 需要非内置的其他箱子,这时,只能说句“抱歉,要本机下载”。
易
- [x]
>
hello world - [x]
>
千兆秒 >< Gigasecond) - [x]
>
闰年 >< Leap - [x]
>
雨滴声 >< Raindrops - [x]
>
反转字符串 >< Reverse String - [x]
>
第 n 个素数 >< Nth Prime - [x]
>
迟钝孩子 >< Bob - [x]
>
啤酒之歌 >< Beer Song - [x]
>
谚语串烧 >< Proverb - [x]
>
平方差 >< Difference Of Squares - [x]
>
倍数之和 >< Sum Of Multiples - [x]
>
谷物 >< Grains - [x]
>
勾股数 >< Pythagorean Triplet - [x]
>
素数因子 >< Prime Factors - [x]
>
子串 >< Series - [x]
>
水仙花数 >< Armstrong Numbers - [x]
>
3n+1 猜想 >< Collatz Conjecture - [x]
>
迪菲-赫尔曼密钥交换 >< Diffie Hellman
中等
- [x]
>
- 鞍点 >< Saddle Points - [x]
>
等值线 >< Isogram - [x]
>
英文说数字 >< Say - [x]
>
游程编码 >< Run Length Encoding - [x]
>
图书编号 >< ISBN Verifier - [x]
>
数字也能分类 >< Perfect Numbers - [x]
>
时钟 >< Clock - [x]
no
DOT DSL - [x]
>
汉明距离 >< Hamming - [x]
>
简单链表 >< Simple Linked List - [x]
>
杨辉三角形 >< Pascal’s Triangle - [x]
>
字母的分数游戏 >< Scrabble Score - [x]
>
全字母句 >< Pangram - [x]
no
PaaS-IO-报告 >< Paasio - [x]
>
核苷酸计数 >< Nucleotide Count - [x]
>
模 10 算法 >< Luhn - [x]
>
最大数字子串乘积 >< Largest Series Product - [x]
>
单词计数 >< Word Count - [x]
>
Atbash 加密 >< Atbash Cipher - [x]
>
密码矩形 >< Crypto Square - [x]
>
旋转密码 >< Rotational Cipher - [x]
>
简单加密 >< Simple Cipher - [x]
>
栅栏密码 >< Rail Fence Cipher - [x]
>
ETL - [x]
>
集合操作 >< Accumulate - [x]
>
术语 >< Acronym - [x]
>
素数筛 >< Sieve - [x]
>
RNA 转录 >< RNA Transcription - [x]
>
三角形 - [x]
>
罗马数字 >< Roman Numerals - [x]
>
你所的基本 - [x]
>
学册 - [x]
>
二分查找 - [x]
>
机器人模拟器 - [x]
>
括号配套 - [x]
>
Luhn From - [x]
>
皇后 攻击 - [x]
>
保龄球 - [x]
>
子列表 - [x]
>
地球年 - [x]
>
Luhn Trait - [x]
>
宏 - [x]
>
过敏 - [x]
>
可变长度数量 - [x]
>
电话号码 - [x]
>
罗唆 - [x]
>
比赛 - [x]
>
自定义 set - [x]
no
字母谜题 - [x]
>
两个桶 - [x]
>
猪的拉丁文 - [x]
>
钻石 - [x]
>
螺旋矩阵 - [x]
>
回文产品 - [x]
no
扑克 - [x]
>
grep - [x]
no
音阶生成器 - [x]
no
十进制 - [x]
>
字谜 - [x]
>
蛋白质翻译 - [x]
>
机器人名称 - [x]
>
书店
难
还没标签
易
- hello world
- 千兆秒-Gigasecond
- 闰年-Leap
- 雨滴声-Raindrops
- 反转字符串-Reverse String
- 第n个素数-Nth Prime
- 迟钝孩子-Bob
- 啤酒之歌-Beer Song
- 谚语串烧-Proverb
- 平方差-Difference Of Squares
- Sum Of Multiples
- Grains
- Pythagorean Triplet
- Prime Factors
- Series
- Armstrong Numbers
- Collatz Conjecture
- Diffie Hellman
hello world
1. Readme
你好世界
经典的介绍性练习.只要说”Hello, World!”.
- 如有需要请看小小的入门
“Hello, World!”是在新的语言或环境中开始编程的第一个程序的传统.
目标很简单:
- 编写一个返回字符串”Hello,World!”的函数.
- 运行测试套件,并确保测试成功.
- 提交您的解决方案,并在网站上查看.
如果一切顺利,你将准备好进行第一次真正的锻炼.
资源
这是一个介绍http://en.wikipedia.org/wiki/%22Hello,_world!%22_program使用者,使用Exercism进行练习.
2. 开始你的表演
// The &'static here means the return type has a static lifetime. // This is a Rust feature that you don't need to worry about now. pub fn hello() -> &'static str { "Goodbye, World!" }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_hello_world() { assert_eq!("Hello, World!", hello()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn hello() -> &'static str { "Hello, World!" } #}
填充/相关
千兆秒-Gigasecond
1. Readme
Gigasecond
计算某个开始时刻,计算10^9秒后的时刻.
一个 千兆秒-gigasecond
是10^9(1,000,000,000)秒.
如果您不确定DateTime<Utc>
可以执行哪些操作,看看chrono crate - 它在Cargo.toml
,被列为本练习的一个依赖项.
Source
Chapter 9 in Chris Pine’s online Learn to Program tutorial. http://pine.fm/LearnToProgram/?Chapter=09
2. 开始你的表演
extern crate chrono; use chrono::{DateTime, Utc}; // Returns a Utc DateTime one billion seconds after start. pub fn after(start: DateTime<Utc>) -> DateTime<Utc> { unimplemented!("What time is a gigasecond later than {}", start); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // extern crate chrono; use chrono::TimeZone; #[test] fn test_date() { let start_date = Utc.ymd(2011, 4, 25).and_hms(0, 0, 0); assert_eq!(after(start_date), Utc.ymd(2043, 1, 1).and_hms(1, 46, 40)); } #[test] //#[ignore] fn test_another_date() { let start_date = Utc.ymd(1977, 6, 13).and_hms(0, 0, 0); assert_eq!(after(start_date), Utc.ymd(2009, 2, 19).and_hms(1, 46, 40)); } #[test] //#[ignore] fn test_third_date() { let start_date = Utc.ymd(1959, 7, 19).and_hms(0, 0, 0); assert_eq!(after(start_date), Utc.ymd(1991, 3, 27).and_hms(1, 46, 40)); } #[test] //#[ignore] fn test_datetime() { let start_date = Utc.ymd(2015, 1, 24).and_hms(22, 0, 0); assert_eq!(after(start_date), Utc.ymd(2046, 10, 2).and_hms(23, 46, 40)); } #[test] //#[ignore] fn test_another_datetime() { let start_date = Utc.ymd(2015, 1, 24).and_hms(23, 59, 59); assert_eq!(after(start_date), Utc.ymd(2046, 10, 3).and_hms(1, 46, 39)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { extern crate chrono; use chrono::{DateTime, Duration, Utc}; pub fn after(start: DateTime<Utc>) -> DateTime<Utc> { start + Duration::seconds(1_000_000_000) } #}
填充/相关
闰年-Leap
1. Readme
Leap
给出年份,报告是不是闰年.
这里的棘手问题是公历中,闰年计算:
我想也很清楚了,就不译了
on every year that is evenly divisible by 4
except every year that is evenly divisible by 100
unless the year is also evenly divisible by 400
例如,1997不是闰年,而是1996是,1900不是闰年,而2000是。
如果在您的语言标准库中,提供了执行此实现的方法, 请假装它不存在,并自己实现它.
笔记
虽然我们的采用一些非常简单的规则,但还有更多的东西要学!
为了一个令人愉快的,为什么有闰年现象的解释,请观看这个YouTube视频.
Source
JavaRanch Cattle Drive, exercise 3 http://www.javaranch.com/leap.jsp
2. 开始你的表演
pub fn is_leap_year(year: i32) -> bool { unimplemented!("true if {} is a leap year", year) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_vanilla_leap_year() { assert_eq!(is_leap_year(1996), true); } #[test] //#[ignore] fn test_any_old_year() { assert_eq!(is_leap_year(1997), false); } #[test] //#[ignore] fn test_century() { assert_eq!(is_leap_year(1700), false); assert_eq!(is_leap_year(1800), false); assert_eq!(is_leap_year(1900), false); } #[test] //#[ignore] fn test_exceptional_centuries() { assert_eq!(is_leap_year(1600), true); assert_eq!(is_leap_year(2000), true); assert_eq!(is_leap_year(2400), true); } #[test] //#[ignore] fn test_years_1600_to_1699() { let incorrect_years = (1600..1700) .filter(|&year| is_leap_year(year) != (year % 4 == 0)) .collect::<Vec<_>>(); if !incorrect_years.is_empty() { panic!("incorrect result for years: {:?}", incorrect_years); } } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn is_leap_year(year: i32) -> bool { let has_factor = |n| year % n == 0; has_factor(4) && (!has_factor(100) || has_factor(400)) } #}
填充/相关
雨滴声-Raindrops
1. Readme
雨滴-Raindrops
把一个数字转换成一个字符串,它的内容取决于,数字的因素.
- 如果数字有 3 作为一个因素,输出”Pling”.
- 如果数字有 5 作为一个因素,输出”Plang’”.
- 如果数字有 7 作为一个因素,输出”Plong”.
- 如果数字没有 3, 5,或 7 作为一个因素, 直接给数字。
实例
- 28 的因素是 1, 2, 4, 7,14, 28.
- 雨滴说,这将是一个简单的”Plong”.
- 30 的因素是 1, 2,3,5,6, 10, 15,30.
- 雨滴说,这将是一个”PlingPlang”.
- 34 有四个因素:1, 2, 17,34.
- 雨滴说,这将是”34”.
Source
A variation on a famous interview question intended to weed out potential candidates. http://jumpstartlab.com
2. 开始你的表演
pub fn raindrops(n: u32) -> String { unimplemented!("what sound does Raindrop #{} make?", n) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_1() { assert_eq!("1", raindrops(1)); } #[test] //#[ignore] fn test_3() { assert_eq!("Pling", raindrops(3)); } #[test] //#[ignore] fn test_5() { assert_eq!("Plang", raindrops(5)); } #[test] //#[ignore] fn test_7() { assert_eq!("Plong", raindrops(7)); } #[test] //#[ignore] fn test_6() { assert_eq!("Pling", raindrops(6)); } #[test] //#[ignore] fn test_8() { assert_eq!("8", raindrops(8)); } #[test] //#[ignore] fn test_9() { assert_eq!("Pling", raindrops(9)); } #[test] //#[ignore] fn test_10() { assert_eq!("Plang", raindrops(10)); } #[test] //#[ignore] fn test_14() { assert_eq!("Plong", raindrops(14)); } #[test] //#[ignore] fn test_15() { assert_eq!("PlingPlang", raindrops(15)); } #[test] //#[ignore] fn test_21() { assert_eq!("PlingPlong", raindrops(21)); } #[test] //#[ignore] fn test_25() { assert_eq!("Plang", raindrops(25)); } #[test] //#[ignore] fn test_27() { assert_eq!("Pling", raindrops(27)); } #[test] //#[ignore] fn test_35() { assert_eq!("PlangPlong", raindrops(35)); } #[test] //#[ignore] fn test_49() { assert_eq!("Plong", raindrops(49)); } #[test] //#[ignore] fn test_52() { assert_eq!("52", raindrops(52)); } #[test] //#[ignore] fn test_105() { assert_eq!("PlingPlangPlong", raindrops(105)); } #[test] //#[ignore] fn test_3125() { assert_eq!("Plang", raindrops(3125)); } #[test] //#[ignore] fn test_12121() { assert_eq!("12121", raindrops(12121)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn raindrops(n: u32) -> String { let is_pling = |n| n % 3 == 0; let is_plang = |n| n % 5 == 0; let is_plong = |n| n % 7 == 0; let mut drops = String::new(); if is_pling(n) { drops.push_str("Pling"); } if is_plang(n) { drops.push_str("Plang"); } if is_plong(n) { drops.push_str("Plong"); } if drops.is_empty() { let s = format!("{}", n); drops.push_str(&s); } drops } #}
填充/相关
反转字符串-Reverse String
1. Readme
反向的字符串
反向的字符串
例如:
- input: “cool”
- output: “looc”
加分
用这个字符串:uüu
测试你的函数, 看会发生什么。试着写一个函数,这恰当地反转这个字符串。提示: {grapheme clusters
}
要拿到加分,需要测试加分项, 从最后一个测试中移除(#[ignore]
)标志),并运行下面测试命令:
$ cargo test --features grapheme
Source
Introductory challenge to reverse an input string https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb
2. 开始你的表演
pub fn reverse(input: &str) -> String { unimplemented!("Write a function to reverse {}", input); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { /// Tests for reverse-string /// /// Generated by [script][script] using [canonical data][canonical-data] /// /// [script]: https://github.com/exercism/rust/blob/master/bin/init_exercise.py /// [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/reverse-string/canonical_data.json /// Process a single test case for the property `reverse` fn process_reverse_case(input: &str, expected: &str) { assert_eq!(&reverse(input), expected) } #[test] /// empty string fn test_empty_string() { process_reverse_case("", ""); } #[test] //#[ignore] /// a word fn test_a_word() { process_reverse_case("robot", "tobor"); } #[test] //#[ignore] /// a capitalized word fn test_a_capitalized_word() { process_reverse_case("Ramen", "nemaR"); } #[test] //#[ignore] /// a sentence with punctuation fn test_a_sentence_with_punctuation() { process_reverse_case("I'm hungry!", "!yrgnuh m'I"); } #[test] //#[ignore] /// a palindrome fn test_a_palindrome() { process_reverse_case("racecar", "racecar"); } #[test] //#[ignore] /// wide characters fn test_wide_characters() { process_reverse_case("子猫", "猫子"); } #[test] //#[ignore] #[cfg(feature = "grapheme")] /// grapheme clusters fn test_grapheme_clusters() { process_reverse_case("uüu", "uüu"); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { //! Example implementation for reverse-string pub fn reverse(input: &str) -> String { let mut output = String::with_capacity(input.len()); output.extend(input.chars().rev()); output } #}
填充/相关
第n个素数-Nth Prime
1. Readme
第n个素数
给定一个数n
,确定第n
个素数是什么.
如: 列出前六个素数:2, 3, 5、7, 11和13
,我们可以看到第六素数是13
.
如果您的语言标准库中提供了处理素数的方法,请假装它们不存在,并自己实现它们.
Source
A variation on Problem 7 at Project Euler http://projecteuler.net/problem=7
2. 开始你的表演
pub fn nth(n: u32) -> u32 { unimplemented!("What is the 0-indexed {}th prime number?", n) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_first_prime() { assert_eq!(nth(0), 2); } #[test] //#[ignore] fn test_second_prime() { assert_eq!(nth(1), 3); } #[test] //#[ignore] fn test_sixth_prime() { assert_eq!(nth(5), 13); } #[test] //#[ignore] fn test_big_prime() { assert_eq!(nth(10000), 104743); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { fn is_prime(n: u32) -> bool { let mut i = 3; while (i * i) < (n + 1) { if n % i == 0 { return false; } i += 1; } return true; } pub fn nth(n: u32) -> u32 { if n == 0 { 2 } else { let mut count = 0; let mut candidate = 1; while count < n { candidate += 2; if is_prime(candidate) { count += 1; } } candidate } } #}
填充/相关
迟钝孩子-Bob
1. Readme
鲍勃-bob
鲍伯是一个懒散的青少年.在谈话中,他的反应非常有限.
-
鲍伯回答:”Sure.”,如果你问他一个问题.
-
他回答:”Whoa, chill out!”,如果你对他大喊大叫.
-
他回答”Calm down, I know what I’m doing!”,如果你大声问他问题.
-
他说”Fine. Be that way!”,如果你喊他,而不说任何话.
-
他回答”Whatever”,给剩下的对话
鲍勃的对话伙伴,在书面交流方面是一个纯粹主义者,并且总是遵循关于 英语句子标点 的通用规则.
Source
Inspired by the ‘Deaf Grandma’ exercise in Chris Pine’s Learn to Program tutorial. http://pine.fm/LearnToProgram/?Chapter=06
2. 开始你的表演
pub fn reply(message: &str) -> &str { unimplemented!("have Bob reply to the incoming message: {}", message) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_stating_something() { assert_eq!("Whatever.", reply("Tom-ay-to, tom-aaaah-to.")); } #[test] //#[ignore] fn test_shouting() { assert_eq!("Whoa, chill out!", reply("WATCH OUT!")); } #[test] //#[ignore] fn test_shouting_gibberish() { assert_eq!("Whoa, chill out!", reply("FCECDFCAAB")); } #[test] //#[ignore] fn test_asking() { assert_eq!( "Sure.", reply("Does this cryogenic chamber make me look fat?") ); } #[test] //#[ignore] fn test_ask_numeric_question() { assert_eq!("Sure.", reply("You are, what, like 15?")); } #[test] //#[ignore] fn test_asking_gibberish() { assert_eq!("Sure.", reply("fffbbcbeab?")); } #[test] //#[ignore] fn test_exclaiming() { assert_eq!("Whatever.", reply("Let's go make out behind the gym!")); } #[test] //#[ignore] fn test_using_acronyms_in_regular_speech() { assert_eq!( "Whatever.", reply("It's OK if you don't want to go to the DMV.") ); } #[test] //#[ignore] fn test_forceful_question() { assert_eq!( "Calm down, I know what I'm doing!", reply("WHAT THE HELL WERE YOU THINKING?") ); } #[test] //#[ignore] fn test_shouting_numbers() { assert_eq!("Whoa, chill out!", reply("1, 2, 3 GO!")); } #[test] //#[ignore] fn test_only_numbers() { assert_eq!("Whatever.", reply("1, 2, 3")); } #[test] //#[ignore] fn test_question_with_only_numbers() { assert_eq!("Sure.", reply("4?")); } #[test] //#[ignore] fn test_shouting_with_special_characters() { assert_eq!( "Whoa, chill out!", reply("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!") ); } #[test] //#[ignore] fn test_shouting_with_no_exclamation_mark() { assert_eq!("Whoa, chill out!", reply("I HATE YOU")); } #[test] //#[ignore] fn test_statement_containing_question_mark() { assert_eq!("Whatever.", reply("Ending with ? means a question.")); } #[test] //#[ignore] fn test_non_letters_with_question() { assert_eq!("Sure.", reply(":) ?")); } #[test] //#[ignore] fn test_prattling_on() { assert_eq!("Sure.", reply("Wait! Hang on. Are you going to be OK?")); } #[test] //#[ignore] fn test_silence() { assert_eq!("Fine. Be that way!", reply("")); } #[test] //#[ignore] fn test_prolonged_silence() { assert_eq!("Fine. Be that way!", reply(" ")); } #[test] //#[ignore] fn test_alternate_silence() { assert_eq!("Fine. Be that way!", reply("\t\t\t\t\t\t\t\t\t\t")); } #[test] //#[ignore] fn test_multiple_line_question() { assert_eq!( "Whatever.", reply("\nDoes this cryogenic chamber make me look fat?\nno") ); } #[test] //#[ignore] fn test_starting_with_whitespace() { assert_eq!("Whatever.", reply(" hmmmmmmm...")); } #[test] //#[ignore] fn test_ending_with_whitespace() { assert_eq!("Sure.", reply("Okay if like my spacebar quite a bit? ")); } #[test] //#[ignore] fn test_other_whitespace() { assert_eq!("Fine. Be that way!", reply("\n\r \t")); } #[test] //#[ignore] fn test_non_question_ending_with_whitespace() { assert_eq!( "Whatever.", reply("This is a statement ending with whitespace ") ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn reply(message: &str) -> &str { if is_silence(message) { "Fine. Be that way!" } else if is_yelling(message) && is_question(message) { "Calm down, I know what I'm doing!" } else if is_yelling(message) { "Whoa, chill out!" } else if is_question(message) { "Sure." } else { "Whatever." } } fn is_silence(message: &str) -> bool { message.trim().is_empty() } fn is_yelling(message: &str) -> bool { let s = message.trim_matches(|c: char| !c.is_alphabetic()); !s.is_empty() && s.to_uppercase() == s } fn is_question(message: &str) -> bool { message.trim_right().ends_with("?") } #}
填充/相关
啤酒之歌-Beer Song
1. Readme
啤酒之歌
背诵那首备受喜爱的经典歌词,即去实地考察时的最爱: 墙上的 99 瓶啤酒.
请注意,并非所有歌词都相同.
99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.
98 bottles of beer on the wall, 98 bottles of beer.
Take one down and pass it around, 97 bottles of beer on the wall.
97 bottles of beer on the wall, 97 bottles of beer.
Take one down and pass it around, 96 bottles of beer on the wall.
96 bottles of beer on the wall, 96 bottles of beer.
Take one down and pass it around, 95 bottles of beer on the wall.
95 bottles of beer on the wall, 95 bottles of beer.
Take one down and pass it around, 94 bottles of beer on the wall.
94 bottles of beer on the wall, 94 bottles of beer.
Take one down and pass it around, 93 bottles of beer on the wall.
93 bottles of beer on the wall, 93 bottles of beer.
Take one down and pass it around, 92 bottles of beer on the wall.
92 bottles of beer on the wall, 92 bottles of beer.
Take one down and pass it around, 91 bottles of beer on the wall.
91 bottles of beer on the wall, 91 bottles of beer.
Take one down and pass it around, 90 bottles of beer on the wall.
90 bottles of beer on the wall, 90 bottles of beer.
Take one down and pass it around, 89 bottles of beer on the wall.
89 bottles of beer on the wall, 89 bottles of beer.
Take one down and pass it around, 88 bottles of beer on the wall.
88 bottles of beer on the wall, 88 bottles of beer.
Take one down and pass it around, 87 bottles of beer on the wall.
87 bottles of beer on the wall, 87 bottles of beer.
Take one down and pass it around, 86 bottles of beer on the wall.
86 bottles of beer on the wall, 86 bottles of beer.
Take one down and pass it around, 85 bottles of beer on the wall.
85 bottles of beer on the wall, 85 bottles of beer.
Take one down and pass it around, 84 bottles of beer on the wall.
84 bottles of beer on the wall, 84 bottles of beer.
Take one down and pass it around, 83 bottles of beer on the wall.
83 bottles of beer on the wall, 83 bottles of beer.
Take one down and pass it around, 82 bottles of beer on the wall.
82 bottles of beer on the wall, 82 bottles of beer.
Take one down and pass it around, 81 bottles of beer on the wall.
81 bottles of beer on the wall, 81 bottles of beer.
Take one down and pass it around, 80 bottles of beer on the wall.
80 bottles of beer on the wall, 80 bottles of beer.
Take one down and pass it around, 79 bottles of beer on the wall.
79 bottles of beer on the wall, 79 bottles of beer.
Take one down and pass it around, 78 bottles of beer on the wall.
78 bottles of beer on the wall, 78 bottles of beer.
Take one down and pass it around, 77 bottles of beer on the wall.
77 bottles of beer on the wall, 77 bottles of beer.
Take one down and pass it around, 76 bottles of beer on the wall.
76 bottles of beer on the wall, 76 bottles of beer.
Take one down and pass it around, 75 bottles of beer on the wall.
75 bottles of beer on the wall, 75 bottles of beer.
Take one down and pass it around, 74 bottles of beer on the wall.
74 bottles of beer on the wall, 74 bottles of beer.
Take one down and pass it around, 73 bottles of beer on the wall.
73 bottles of beer on the wall, 73 bottles of beer.
Take one down and pass it around, 72 bottles of beer on the wall.
72 bottles of beer on the wall, 72 bottles of beer.
Take one down and pass it around, 71 bottles of beer on the wall.
71 bottles of beer on the wall, 71 bottles of beer.
Take one down and pass it around, 70 bottles of beer on the wall.
70 bottles of beer on the wall, 70 bottles of beer.
Take one down and pass it around, 69 bottles of beer on the wall.
69 bottles of beer on the wall, 69 bottles of beer.
Take one down and pass it around, 68 bottles of beer on the wall.
68 bottles of beer on the wall, 68 bottles of beer.
Take one down and pass it around, 67 bottles of beer on the wall.
67 bottles of beer on the wall, 67 bottles of beer.
Take one down and pass it around, 66 bottles of beer on the wall.
66 bottles of beer on the wall, 66 bottles of beer.
Take one down and pass it around, 65 bottles of beer on the wall.
65 bottles of beer on the wall, 65 bottles of beer.
Take one down and pass it around, 64 bottles of beer on the wall.
64 bottles of beer on the wall, 64 bottles of beer.
Take one down and pass it around, 63 bottles of beer on the wall.
63 bottles of beer on the wall, 63 bottles of beer.
Take one down and pass it around, 62 bottles of beer on the wall.
62 bottles of beer on the wall, 62 bottles of beer.
Take one down and pass it around, 61 bottles of beer on the wall.
61 bottles of beer on the wall, 61 bottles of beer.
Take one down and pass it around, 60 bottles of beer on the wall.
60 bottles of beer on the wall, 60 bottles of beer.
Take one down and pass it around, 59 bottles of beer on the wall.
59 bottles of beer on the wall, 59 bottles of beer.
Take one down and pass it around, 58 bottles of beer on the wall.
58 bottles of beer on the wall, 58 bottles of beer.
Take one down and pass it around, 57 bottles of beer on the wall.
57 bottles of beer on the wall, 57 bottles of beer.
Take one down and pass it around, 56 bottles of beer on the wall.
56 bottles of beer on the wall, 56 bottles of beer.
Take one down and pass it around, 55 bottles of beer on the wall.
55 bottles of beer on the wall, 55 bottles of beer.
Take one down and pass it around, 54 bottles of beer on the wall.
54 bottles of beer on the wall, 54 bottles of beer.
Take one down and pass it around, 53 bottles of beer on the wall.
53 bottles of beer on the wall, 53 bottles of beer.
Take one down and pass it around, 52 bottles of beer on the wall.
52 bottles of beer on the wall, 52 bottles of beer.
Take one down and pass it around, 51 bottles of beer on the wall.
51 bottles of beer on the wall, 51 bottles of beer.
Take one down and pass it around, 50 bottles of beer on the wall.
50 bottles of beer on the wall, 50 bottles of beer.
Take one down and pass it around, 49 bottles of beer on the wall.
49 bottles of beer on the wall, 49 bottles of beer.
Take one down and pass it around, 48 bottles of beer on the wall.
48 bottles of beer on the wall, 48 bottles of beer.
Take one down and pass it around, 47 bottles of beer on the wall.
47 bottles of beer on the wall, 47 bottles of beer.
Take one down and pass it around, 46 bottles of beer on the wall.
46 bottles of beer on the wall, 46 bottles of beer.
Take one down and pass it around, 45 bottles of beer on the wall.
45 bottles of beer on the wall, 45 bottles of beer.
Take one down and pass it around, 44 bottles of beer on the wall.
44 bottles of beer on the wall, 44 bottles of beer.
Take one down and pass it around, 43 bottles of beer on the wall.
43 bottles of beer on the wall, 43 bottles of beer.
Take one down and pass it around, 42 bottles of beer on the wall.
42 bottles of beer on the wall, 42 bottles of beer.
Take one down and pass it around, 41 bottles of beer on the wall.
41 bottles of beer on the wall, 41 bottles of beer.
Take one down and pass it around, 40 bottles of beer on the wall.
40 bottles of beer on the wall, 40 bottles of beer.
Take one down and pass it around, 39 bottles of beer on the wall.
39 bottles of beer on the wall, 39 bottles of beer.
Take one down and pass it around, 38 bottles of beer on the wall.
38 bottles of beer on the wall, 38 bottles of beer.
Take one down and pass it around, 37 bottles of beer on the wall.
37 bottles of beer on the wall, 37 bottles of beer.
Take one down and pass it around, 36 bottles of beer on the wall.
36 bottles of beer on the wall, 36 bottles of beer.
Take one down and pass it around, 35 bottles of beer on the wall.
35 bottles of beer on the wall, 35 bottles of beer.
Take one down and pass it around, 34 bottles of beer on the wall.
34 bottles of beer on the wall, 34 bottles of beer.
Take one down and pass it around, 33 bottles of beer on the wall.
33 bottles of beer on the wall, 33 bottles of beer.
Take one down and pass it around, 32 bottles of beer on the wall.
32 bottles of beer on the wall, 32 bottles of beer.
Take one down and pass it around, 31 bottles of beer on the wall.
31 bottles of beer on the wall, 31 bottles of beer.
Take one down and pass it around, 30 bottles of beer on the wall.
30 bottles of beer on the wall, 30 bottles of beer.
Take one down and pass it around, 29 bottles of beer on the wall.
29 bottles of beer on the wall, 29 bottles of beer.
Take one down and pass it around, 28 bottles of beer on the wall.
28 bottles of beer on the wall, 28 bottles of beer.
Take one down and pass it around, 27 bottles of beer on the wall.
27 bottles of beer on the wall, 27 bottles of beer.
Take one down and pass it around, 26 bottles of beer on the wall.
26 bottles of beer on the wall, 26 bottles of beer.
Take one down and pass it around, 25 bottles of beer on the wall.
25 bottles of beer on the wall, 25 bottles of beer.
Take one down and pass it around, 24 bottles of beer on the wall.
24 bottles of beer on the wall, 24 bottles of beer.
Take one down and pass it around, 23 bottles of beer on the wall.
23 bottles of beer on the wall, 23 bottles of beer.
Take one down and pass it around, 22 bottles of beer on the wall.
22 bottles of beer on the wall, 22 bottles of beer.
Take one down and pass it around, 21 bottles of beer on the wall.
21 bottles of beer on the wall, 21 bottles of beer.
Take one down and pass it around, 20 bottles of beer on the wall.
20 bottles of beer on the wall, 20 bottles of beer.
Take one down and pass it around, 19 bottles of beer on the wall.
19 bottles of beer on the wall, 19 bottles of beer.
Take one down and pass it around, 18 bottles of beer on the wall.
18 bottles of beer on the wall, 18 bottles of beer.
Take one down and pass it around, 17 bottles of beer on the wall.
17 bottles of beer on the wall, 17 bottles of beer.
Take one down and pass it around, 16 bottles of beer on the wall.
16 bottles of beer on the wall, 16 bottles of beer.
Take one down and pass it around, 15 bottles of beer on the wall.
15 bottles of beer on the wall, 15 bottles of beer.
Take one down and pass it around, 14 bottles of beer on the wall.
14 bottles of beer on the wall, 14 bottles of beer.
Take one down and pass it around, 13 bottles of beer on the wall.
13 bottles of beer on the wall, 13 bottles of beer.
Take one down and pass it around, 12 bottles of beer on the wall.
12 bottles of beer on the wall, 12 bottles of beer.
Take one down and pass it around, 11 bottles of beer on the wall.
11 bottles of beer on the wall, 11 bottles of beer.
Take one down and pass it around, 10 bottles of beer on the wall.
10 bottles of beer on the wall, 10 bottles of beer.
Take one down and pass it around, 9 bottles of beer on the wall.
9 bottles of beer on the wall, 9 bottles of beer.
Take one down and pass it around, 8 bottles of beer on the wall.
8 bottles of beer on the wall, 8 bottles of beer.
Take one down and pass it around, 7 bottles of beer on the wall.
7 bottles of beer on the wall, 7 bottles of beer.
Take one down and pass it around, 6 bottles of beer on the wall.
6 bottles of beer on the wall, 6 bottles of beer.
Take one down and pass it around, 5 bottles of beer on the wall.
5 bottles of beer on the wall, 5 bottles of beer.
Take one down and pass it around, 4 bottles of beer on the wall.
4 bottles of beer on the wall, 4 bottles of beer.
Take one down and pass it around, 3 bottles of beer on the wall.
3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.
1 bottle of beer on the wall, 1 bottle of beer.
Take it down and pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
加分
你是否通过了测试,并且代码干净了? 如果您愿意,可以尝试以下加分项:
- 尽可能多地删除重复.
- 优化可读性,即使它意味着引入重复.
- 如果您删除了所有重复项,那么您是否有很多条件? 如果它适用于这种语言,请尝试用多态替换条件语句。它的可读性如何?
那么请在提交的评论中,分享您的想法.这个实验是否让代码更好? 更差?你从中学到了什么?
Source
Learn to Program by Chris Pine http://pine.fm/LearnToProgram/?Chapter=06
2. 开始你的表演
pub fn verse(n: i32) -> String { unimplemented!("emit verse {}", n) } pub fn sing(start: i32, end: i32) -> String { unimplemented!("sing verses {} to {}, inclusive", start, end) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_verse_0() { assert_eq!(verse(0), "No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n"); } #[test] //#[ignore] fn test_verse_1() { assert_eq!(verse(1), "1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n"); } #[test] //#[ignore] fn test_verse_2() { assert_eq!(verse(2), "2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n"); } #[test] //#[ignore] fn test_verse_8() { assert_eq!(verse(8), "8 bottles of beer on the wall, 8 bottles of beer.\nTake one down and pass it around, 7 bottles of beer on the wall.\n"); } #[test] //#[ignore] fn test_song_8_6() { assert_eq!(sing(8, 6), "8 bottles of beer on the wall, 8 bottles of beer.\nTake one down and pass it around, 7 bottles of beer on the wall.\n\n7 bottles of beer on the wall, 7 bottles of beer.\nTake one down and pass it around, 6 bottles of beer on the wall.\n\n6 bottles of beer on the wall, 6 bottles of beer.\nTake one down and pass it around, 5 bottles of beer on the wall.\n"); } #[test] //#[ignore] fn test_song_3_0() { assert_eq!(sing(3, 0), "3 bottles of beer on the wall, 3 bottles of beer.\nTake one down and pass it around, 2 bottles of beer on the wall.\n\n2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n\n1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n\nNo more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n"); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn verse(n: i32) -> String { match n { 0 => "No more bottles of beer on the wall, no more bottles of beer.\n\ Go to the store and buy some more, 99 bottles of beer on the wall.\n" .to_string(), 1 => "1 bottle of beer on the wall, 1 bottle of beer.\n\ Take it down and pass it around, no more bottles of beer on the wall.\n" .to_string(), 2 => "2 bottles of beer on the wall, 2 bottles of beer.\n\ Take one down and pass it around, 1 bottle of beer on the wall.\n" .to_string(), n if n > 2 && n <= 99 => format!( "{n} bottles of beer on the wall, {n} bottles of beer.\n\ Take one down and pass it around, {n_minus_1} bottles of beer on the wall.\n", n = n, n_minus_1 = n - 1 ), _ => panic!(), } } pub fn sing(start: i32, end: i32) -> String { (end..start + 1) .rev() .map(|n| verse(n)) .collect::<Vec<_>>() .join("\n") } #}
填充/相关
谚语串烧-Proverb
1. Readme
谚语
因为没有马蹄钉,一个王国就消失了,或者俗话说.
给出一个输入列表,生成相关的谚语.例如,
- 给定列表
["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]
- 你将输出这个谚语的全文:
For want of a nail the shoe was lost.
For want of a shoe the horse was lost.
For want of a horse the rider was lost.
For want of a rider the message was lost.
For want of a message the battle was lost.
For want of a battle the kingdom was lost.
And all for the want of a nail.
请注意,输入列表可能会有所不同; 您的解决方案应该能够处理任意长度和内容的列表。输出文本的行不应该是静态的、不变的字符串; 所有的都应该根据给定的输入而变化.
Source
Wikipedia http://en.wikipedia.org/wiki/For_Want_of_a_Nail
2. 开始你的表演
pub fn build_proverb(list: Vec<&str>) -> String { unimplemented!("build a proverb from this list of items: {:?}", list) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_two_pieces() { let input = vec!["nail", "shoe"]; let expected = vec![ "For want of a nail the shoe was lost.", "And all for the want of a nail.", ] .join("\n"); assert_eq!(build_proverb(input), expected); } // Notice the change in the last line at three pieces. #[test] //#[ignore] fn test_three_pieces() { let input = vec!["nail", "shoe", "horse"]; let expected = vec![ "For want of a nail the shoe was lost.", "For want of a shoe the horse was lost.", "And all for the want of a nail.", ] .join("\n"); assert_eq!(build_proverb(input), expected); } #[test] //#[ignore] fn test_one_piece() { let input = vec!["nail"]; let expected = String::from("And all for the want of a nail."); assert_eq!(build_proverb(input), expected); } #[test] //#[ignore] fn test_zero_pieces() { let input: Vec<&str> = vec![]; let expected = String::new(); assert_eq!(build_proverb(input), expected); } #[test] //#[ignore] fn test_full() { let input = vec![ "nail", "shoe", "horse", "rider", "message", "battle", "kingdom", ]; let expected = vec![ "For want of a nail the shoe was lost.", "For want of a shoe the horse was lost.", "For want of a horse the rider was lost.", "For want of a rider the message was lost.", "For want of a message the battle was lost.", "For want of a battle the kingdom was lost.", "And all for the want of a nail.", ] .join("\n"); assert_eq!(build_proverb(input), expected); } #[test] //#[ignore] fn test_three_pieces_modernized() { let input = vec!["pin", "gun", "soldier", "battle"]; let expected = vec![ "For want of a pin the gun was lost.", "For want of a gun the soldier was lost.", "For want of a soldier the battle was lost.", "And all for the want of a pin.", ] .join("\n"); assert_eq!(build_proverb(input), expected); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn build_proverb(items: Vec<&str>) -> String { let mut stanzas = Vec::with_capacity(items.len()); for index in 0..items.len() { if index == items.len() - 1 { stanzas.push(format!("And all for the want of a {}.", items[0])); } else { stanzas.push(format!( "For want of a {} the {} was lost.", items[index], items[index + 1] )); } } stanzas.join("\n") } #}
填充/相关
平方差-Difference Of Squares
1. Readme
平方差
求,前n个自然数的和平方,与,平方和,之间的差值.
例如:
前十个自然数之和的平方为:
(1 + 2 + ... + 10)² = 55² = 3025.
前十个自然数的平方和为:
1² + 2² + ... + 10² = 385.
因此,前十个自然数之和的平方,和,前十个自然数之和的平方之差是:
3025 - 385 = 2640.
Source
Problem 6 at Project Euler http://projecteuler.net/problem=6
2. 开始你的表演
pub fn square_of_sum(n: u32) -> u32 { unimplemented!("square of sum of 1...{}", n) } pub fn sum_of_squares(n: u32) -> u32 { unimplemented!("sum of squares of 1...{}", n) } pub fn difference(n: u32) -> u32 { unimplemented!( "difference between square of sum of 1...{n} and sum of squares of 1...{n}", n = n, ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_square_of_sum_1() { assert_eq!(1, square_of_sum(1)); } #[test] //#[ignore] fn test_square_of_sum_5() { assert_eq!(225, square_of_sum(5)); } #[test] //#[ignore] fn test_square_of_sum_100() { assert_eq!(25502500, square_of_sum(100)); } #[test] //#[ignore] fn test_sum_of_squares_1() { assert_eq!(1, sum_of_squares(1)); } #[test] //#[ignore] fn test_sum_of_squares_5() { assert_eq!(55, sum_of_squares(5)); } #[test] //#[ignore] fn test_sum_of_squares_100() { assert_eq!(338350, sum_of_squares(100)); } #[test] //#[ignore] fn test_difference_1() { assert_eq!(0, difference(1)); } #[test] //#[ignore] fn test_difference_5() { assert_eq!(170, difference(5)); } #[test] //#[ignore] fn test_difference_100() { assert_eq!(25164150, difference(100)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn square_of_sum(n: u32) -> u32 { let sum = n * (n + 1) / 2; sum * sum } pub fn sum_of_squares(n: u32) -> u32 { (0..n + 1).map(|x| x * x).fold(0, |accum, x| accum + x) } pub fn difference(n: u32) -> u32 { square_of_sum(n) - sum_of_squares(n) } #}
填充/相关
Sum Of Multiples
1. Readme
倍数之和
给定一个数字,找出另外的特定数字的所有唯一倍数的总和,但不包括第一个数字.
如果我们列出20以下,3或5的倍数的所有自然数,我们得到3,5,6,9,10,12,15和18.
这些倍数的总和是78.
资源
在项目Euler中,问题1的变种http://projecteuler.net/problem=1
2. 开始你的表演
pub fn sum_of_multiples(limit: u32, factors: &[u32]) -> u32 { unimplemented!( "Sum the multiples of all of {:?} which are less than {}", factors, limit ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn multiples_one() { assert_eq!(0, sum_of_multiples(1, &[3, 5])) } #[test] //#[ignore] fn multiples_two() { assert_eq!(3, sum_of_multiples(4, &[3, 5])) } #[test] //#[ignore] fn multiples_three() { assert_eq!(23, sum_of_multiples(10, &[3, 5])) } #[test] //#[ignore] fn multiples_four() { assert_eq!(2318, sum_of_multiples(100, &[3, 5])) } #[test] //#[ignore] fn multiples_five() { assert_eq!(233168, sum_of_multiples(1000, &[3, 5])) } #[test] //#[ignore] fn multiples_six() { assert_eq!(51, sum_of_multiples(20, &[7, 13, 17])) } #[test] //#[ignore] fn multiples_seven() { assert_eq!(30, sum_of_multiples(15, &[4, 6])) } #[test] //#[ignore] fn multiples_eight() { assert_eq!(4419, sum_of_multiples(150, &[5, 6, 8])) } #[test] //#[ignore] fn multiples_nine() { assert_eq!(275, sum_of_multiples(51, &[5, 25])) } #[test] //#[ignore] fn multiples_ten() { assert_eq!(2203160, sum_of_multiples(10000, &[43, 47])) } #[test] //#[ignore] fn multiples_eleven() { assert_eq!(4950, sum_of_multiples(100, &[1])) } #[test] //#[ignore] fn multiples_twelve() { assert_eq!(0, sum_of_multiples(10000, &[])) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::BTreeSet; pub fn sum_of_multiples(limit: u32, factors: &[u32]) -> u32 { let mut multiples: BTreeSet<u32> = BTreeSet::new(); for &f in factors { let mut multiplier = 2; let mut x = f; while x < limit { multiples.insert(x); x = f * multiplier; multiplier += 1; } } multiples.iter().sum() } #}
填充/相关
Grains
1. Readme
谷物
计算棋盘上的小麦粒数,假设每个方格的数量增加一倍.
曾经有一位睿智的仆人拯救了王子的生命。国王承诺支付仆人梦寐以求的一切。知道国王喜欢国际象棋,仆人告诉国王他想吃小麦粒,在棋盘的第一个正方形上放一粒小麦。而接下来的方格是两粒.四粒小麦放在第三格,依此类推.
棋盘上有 64 个方格.
编写代码,用来显示:
- 每个方格上有多少谷物,和
- 谷物总数
加分
你是否通过了测试,并且代码干净了? 如果您愿意,可以尝试以下一些额外的事情:
- 优化速度.
- 优化可读性.
那么请在提交的评论中分享您的想法.这个实验是否使代码更好? 更差? 你从中学到了什么吗?
资源
JavaRanch Cattle 驱动,练习 6 http://www.javaranch.com/grains.jsp
2. 开始你的表演
pub fn square(s: u32) -> u64 { unimplemented!("grains of rice on square {}", s); } pub fn total() -> u64 { unimplemented!(); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn square_one() { assert_eq!(square(1), 1); } #[test] //#[ignore] fn square_two() { assert_eq!(square(2), 2); } #[test] //#[ignore] fn square_three() { assert_eq!(square(3), 4); } #[test] //#[ignore] fn square_four() { assert_eq!(square(4), 8); } #[test] //#[ignore] fn square_sixteen() { assert_eq!(square(16), 32_768); } #[test] //#[ignore] fn square_thirty_two() { assert_eq!(square(32), 2_147_483_648); } #[test] //#[ignore] fn square_sixty_four() { assert_eq!(square(64), 9_223_372_036_854_775_808); } #[test] //#[ignore] #[should_panic(expected = "Square must be between 1 and 64")] fn square_zero_panics() { square(0); } #[test] //#[ignore] #[should_panic(expected = "Square must be between 1 and 64")] fn square_sixty_five_panics() { square(65); } #[test] //#[ignore] fn total_sums_all_squares() { assert_eq!(total(), 18_446_744_073_709_551_615); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn square(s: u32) -> u64 { if s == 0 || s > 64 { panic!("Square must be between 1 and 64"); } 2u64.pow(s - 1) } pub fn total() -> u64 { (1..65).fold(0, |acc, s| acc + square(s)) } #}
填充/相关
Pythagorean Triplet
1. Readme
勾股数
勾股数是一组,三个自然数,{a,b,c}, 如
a**2 + b**2 = c**2
这样,
a < b < c
例如,
3**2 + 4**2 = 9 + 16 = 25 = 5**2.
问: 请找出 a,b,c,恰好符合勾股定理, 而其中 a+b+c=1000.
返回, a * b * c
值.
资源
欧拉项目,问题 9http://projecteuler.net/problem=9
2. 开始你的表演
pub fn find() -> Option<u32> { unimplemented!(); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_answer() { assert_eq!(find(), Some(31875000)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn find() -> Option<u32> { for a in 1..1000 { for b in (a + 1)..(1000 - a) { let c = 1000 - (a + b); if a * a + b * b == c * c { return Some(a * b * c); } } } None } #}
填充/相关
Prime Factors
1. Readme
素数因子
计算给定自然数的素因子.
质数(素数): 只能被自身和 1 整除.
注意,1 不是素数.
例子
60 的主要因素是什么?
- 我们的第一个除数是 2 。 2 被除以 60,剩下 30。
- 2 被除以 30,剩下 15。
- 2 不能被除以 15。让我们转到下一个除数,3.
- 3 被除以 15 分,5 剩下。
- 3 不能被除以 5。下一个可能的因素是 4.
- 4 不能被除以 5。下一个可能的因素是 5.
- 5 确实能被除以 5.
- 我们只剩下 1 ,所以现在,我们完成了。
我们在该计算中成功, 而 60除数代表 ∶ 2, 2, 3 和 5
为主要因子的列表.
你可以自己检查一下:
- 2 × 2 × 3 × 5
- = 4 × 15
- = 60
- 成功!
资源
Uncle Bob 的主要因素算法http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata
2. 开始你的表演
pub fn factors(n: u64) -> Vec<u64> { unimplemented!("This should calculate the prime factors of {}", n) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_no_factors() { assert_eq!(factors(1), vec![]); } #[test] //#[ignore] fn test_prime_number() { assert_eq!(factors(2), vec![2]); } #[test] //#[ignore] fn test_square_of_a_prime() { assert_eq!(factors(9), vec![3, 3]); } #[test] //#[ignore] fn test_cube_of_a_prime() { assert_eq!(factors(8), vec![2, 2, 2]); } #[test] //#[ignore] fn test_product_of_primes_and_non_primes() { assert_eq!(factors(12), vec![2, 2, 3]); } #[test] //#[ignore] fn test_product_of_primes() { assert_eq!(factors(901255), vec![5, 17, 23, 461]); } #[test] //#[ignore] fn test_factors_include_large_prime() { assert_eq!(factors(93819012551), vec![11, 9539, 894119]); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn factors(n: u64) -> Vec<u64> { let mut val = n; let mut out: Vec<u64> = vec![]; let mut possible: u64 = 2; while val > 1 { while val % possible == 0 { out.push(possible); val /= possible; } possible += 1; } out } #}
填充/相关
Series
1. Readme
系列
给定一串数字,输出所有连续的n
长度顺序子串。
例如,字符串”49142”长度为3
的子串系列:
- “491”
- “914”
- “142”
以下长度为 4
的 系列:
- “4914”
- “9142”
如果你要求一个 5 位数字,长度为 6 的系列,你应该得到原数字。
请注意,这些系列只需要在输入中是相邻的位置; 数字不需要在数字上连续.
资源
Project Euler 中,问题 8 的一个子集http://projecteuler.net/problem=8
2. 开始你的表演
pub fn series(digits: &str, len: usize) -> Vec<String> { unimplemented!( "What are the series of length {} in string {:?}", len, digits ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_with_zero_length() { let expected = vec!["".to_string(); 6]; assert_eq!(series("92017", 0), expected); } #[test] //#[ignore] fn test_with_length_2() { let expected = vec![ "92".to_string(), "20".to_string(), "01".to_string(), "17".to_string(), ]; assert_eq!(series("92017", 2), expected); } #[test] //#[ignore] fn test_with_numbers_length() { let expected = vec!["92017".to_string()]; assert_eq!(series("92017", 5), expected); } #[test] //#[ignore] fn test_too_long() { let expected: Vec<String> = vec![]; assert_eq!(series("92017", 6), expected); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn series(digits: &str, len: usize) -> Vec<String> { match len { 0 => vec!["".to_string(); digits.len() + 1], _ => digits .chars() .collect::<Vec<char>>() .windows(len) .map(|window| window.into_iter().collect::<String>()) .collect::<Vec<String>>(), } } #}
填充/相关
Armstrong Numbers
1. Readme
水仙花数
一个Armstrong number是一个数字,它是自身每个单数字与数字量的幂,之和。
例如:
- 9 是水仙花数,因为
9 = 9^1 = 9
- 10 不是一个水仙花数,因为
10 != 1^2 + 0^2 = 1
- 153 是水仙花数,因为:
153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153
- 154 不是一个水仙花数,因为:
154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190
写一些代码来确定一个数字是否水仙花数.
资源
维基百科https://en.wikipedia.org/wiki/Narcissistic_number
2. 开始你的表演
pub fn is_armstrong_number(num: u32) -> bool { unimplemented!("true if {} is an armstrong number", num) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_single_digit_numbers_are_armstrong_numbers() { assert!(is_armstrong_number(5)) } #[test] //#[ignore] fn test_there_are_no_2_digit_armstrong_numbers() { assert!(!is_armstrong_number(10)) } #[test] //#[ignore] fn test_three_digit_armstrong_number() { assert!(is_armstrong_number(153)) } #[test] //#[ignore] fn test_three_digit_non_armstrong_number() { assert!(!is_armstrong_number(100)) } #[test] //#[ignore] fn test_four_digit_armstrong_number() { assert!(is_armstrong_number(9474)) } #[test] //#[ignore] fn test_four_digit_non_armstrong_number() { assert!(!is_armstrong_number(9475)) } #[test] //#[ignore] fn test_seven_digit_armstrong_number() { assert!(is_armstrong_number(9926315)) } #[test] //#[ignore] fn test_seven_digit_non_armstrong_number() { assert!(!is_armstrong_number(9926316)) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn is_armstrong_number(num: u32) -> bool { let s = format!("{}", num); let l = s.len(); s.chars() .map(|c| c.to_digit(10).unwrap().pow(l as u32)) .sum::<u32>() == num } #}
填充/相关
Collatz Conjecture
1. Readme
考拉兹猜想
在 考拉兹猜想 或 3n+1 猜想 可以如下:
是指对于每一个正整数,如果它是奇数,则对它乘 3 再加 1,如果它是偶数,则对它除以 2,如此循环,最终都能够得到 1。
给予一个数字n
,那它到达 1 的步骤.
例子
如 n = 12
,步骤如下:将
- 12
- 6
- 3
- 10
- 5
- 16
- 8
- 4
- 2
- 1
9 步骤。系统的输入是n = 12
,返回值将是9
资源
以数学家 Lothar Collatz 命名的一个未解决的数学问题https://en.wikipedia.org/wiki/3x_%2B_1_problem
2. 开始你的表演
pub fn collatz(n: u64) -> Option<u64> { unimplemented!( "return Some(x) where x is the number of steps required to reach 1 starting with {}", n, ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_1() { assert_eq!(Some(0), collatz(1)); } #[test] //#[ignore] fn test_16() { assert_eq!(Some(4), collatz(16)); } #[test] //#[ignore] fn test_12() { assert_eq!(Some(9), collatz(12)); } #[test] //#[ignore] fn test_1000000() { assert_eq!(Some(152), collatz(1000000)); } #[test] //#[ignore] fn test_0() { assert_eq!(None, collatz(0)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn collatz_positive(n: u64) -> u64 { if n == 1 { 0 } else { 1 + match n % 2 { 0 => collatz_positive(n / 2), _ => collatz_positive(n * 3 + 1), } } } // return Ok(x) where x is the number of steps required to reach 1 pub fn collatz(n: u64) -> Option<u64> { if n < 1 { None } else { Some(collatz_positive(n)) } } #}
填充/相关
Diffie Hellman
1. Readme
迪菲-赫尔曼
迪菲-赫尔曼密钥交换
Alice 和 Bob 使用 迪菲-赫尔曼密钥来共享秘密。它们以素数开头,选择私钥,生成和共享公钥,然后生成共享密钥.
第 0 步
测试程序提供素数 p 和 g
.
步骤 1
Alice 选择一个大于 1 ,且小于 p 的私钥。鲍勃做同样的事情来选择私钥 b.
第 2 步
Alice 计算公钥 A.
A = g**a mod p
使用相同的 p 和 g, Bob 类似地从他的私钥 b 计算公钥 B.
第 3 步
Alice 和 Bob 交换公钥.Alice 计算密钥 s.
s = B**a mod p
鲍勃计算
s = A**b mod p
计算产生相同的结果! 爱丽丝和鲍勃现在分享秘密.
本练习的一种可能解决方案是实现您自己的模幂运算函数。要了解更多信息,请参阅following page.
资源
-
维基百科,来自 www.cryptopp.com/wiki 的 1024 位密钥.http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
2. 开始你的表演
pub fn private_key(p: u64) -> u64 { unimplemented!("Pick a private key greater than 1 and less than {}", p) } pub fn public_key(p: u64, g: u64, a: u64) -> u64 { unimplemented!( "Calculate public key using prime numbers {} and {}, and private key {}", p, g, a ) } pub fn secret(p: u64, b_pub: u64, a: u64) -> u64 { unimplemented!( "Calculate secret key using prime number {}, public key {}, and private key {}", p, b_pub, a ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_private_key_in_range_key() { let primes: Vec<u64> = vec![ 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 773, 967, 3461, 6131, ]; let private_keys: Vec<u64> = primes.iter().map(|x| private_key(*x)).collect(); for i in 0..primes.len() { assert!(1 < private_keys[i] && private_keys[i] < primes[i]); } } #[test] //#[ignore] fn test_public_key_correct() { let p: u64 = 23; let g: u64 = 5; let private_key: u64 = 6; let expected: u64 = 8; assert_eq!(public_key(p, g, private_key), expected); } #[test] //#[ignore] fn test_secret_key_correct() { let p: u64 = 11; let private_key_a = 7; let public_key_b = 8; let secret = secret(p, public_key_b, private_key_a); let expected = 2; assert_eq!(secret, expected); } #[test] //#[ignore] fn test_public_key_correct_big_numbers() { let p: u64 = 4_294_967_299; let g: u64 = 8; let private_key: u64 = 4_294_967_296; let expected: u64 = 4096; assert_eq!(public_key(p, g, private_key), expected); } #[test] //#[ignore] fn test_secret_key_correct_big_numbers() { let p: u64 = 4_294_967_927; let private_key_a = 4_294_967_300; let public_key_b = 843; let secret = secret(p, public_key_b, private_key_a); let expected = 1_389_354_282; assert_eq!(secret, expected); } #[test] //#[ignore] fn test_changed_secret_key() { let p: u64 = 13; let g: u64 = 11; let private_key_a = private_key(p); let private_key_b = private_key(p); let public_key_a = public_key(p, g, private_key_a); let public_key_b = public_key(p, g, private_key_b); // Key exchange let secret_a = secret(p, public_key_b, private_key_a); let secret_b = secret(p, public_key_a, private_key_b); assert_eq!(secret_a, secret_b); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { extern crate rand; use rand::{thread_rng, Rng}; /// Right-to-left modular exponentiation implementation /// For more information see https://en.wikipedia.org/wiki/Modular_exponentiation fn modular_exponentiation(base: u64, exponent: u64, modulus: u64) -> u64 { let mut result = 1; let mut e = exponent; let mut b = base; while e > 0 { if (e & 1) == 1 { result = (result * b) % modulus; } e >>= 1; b = (b * b) % modulus; } result } pub fn private_key(p: u64) -> u64 { let mut rng = thread_rng(); rng.gen_range(2, p) } pub fn public_key(p: u64, g: u64, a: u64) -> u64 { modular_exponentiation(g, a, p) } pub fn secret(p: u64, b_pub: u64, a: u64) -> u64 { modular_exponentiation(b_pub, a, p) } #}
填充/相关
中等
- Saddle Points
- Isogram
- Say
- Run Length Encoding
- ISBN Verifier
- Perfect Numbers
- Clock
- DOT DSL
- Hamming
- Simple Linked List
- Pascal’s Triangle
- Scrabble Score
- Pangram
- Paasio
- Nucleotide Count
- Luhn
- Largest Series Product
- Word Count
- Atbash Cipher
- Crypto Square
- Rotational Cipher
- Simple Cipher
- Rail Fence Cipher
- ETL
- Accumulate
- Acronym
- Sieve
- RNA Transcription
- Triangle
- Roman Numerals
- All Your Base
- Grade School
- Binary Search
- Robot Simulator
- Bracket Push
- Luhn From
- Queen Attack
- Bowling
- Sublist
- Space Age
- Luhn Trait
- Macros
- Allergies
- Variable Length Quantity
- Phone Number
- Wordy
- Tournament
- Custom Set
- Alphametics
- Two Bucket
- Pig Latin
- Diamond
- Spiral Matrix
- Palindrome Products
- Poker
- Grep
- Scale Generator
- Decimal
- Anagram
- Protein Translation
- Robot Name
Saddle Points
1. Readme
鞍点
检测矩阵中的鞍点(saddle point).
所以说你有一个像这样的矩阵:
0 1 2
|---------
0 | 9 8 7
1 | 5 3 2 <--- saddle point at (1,0)
2 | 6 6 7
它在(1,0)处有一个鞍点.
它被称为”鞍点”,因为它是该 行 最大数,也是该 列 的最小数。
矩阵可以具有零个或多个鞍点.
您的代码应该能够为任何给定矩阵提供所有鞍点的(可能为空)列表。
矩阵可以具有不同数量的行和列(非正方形)。
请注意,您可能会在线找到矩阵鞍点的其他定义,但本练习的测试遵循上述明确的定义.
资源
J Dalbey 的编程实践问题http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html
2. 开始你的表演
pub fn find_saddle_points(input: &[Vec<u64>]) -> Vec<(usize, usize)> { unimplemented!( "find the saddle points of the following matrix: {:?}", input ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // We don't care about order fn find_sorted_saddle_points(input: &[Vec<u64>]) -> Vec<(usize, usize)> { let mut result = find_saddle_points(input); result.sort(); result } #[test] fn identify_single_saddle_point() { let input = vec![vec![9, 8, 7], vec![5, 3, 2], vec![6, 6, 7]]; assert_eq!(vec![(1, 0)], find_saddle_points(&input)); } #[test] //#[ignore] fn identify_empty_matrix() { let input = vec![vec![], vec![], vec![]]; let expected: Vec<(usize, usize)> = Vec::new(); assert_eq!(expected, find_saddle_points(&input)); } #[test] //#[ignore] fn identify_lack_of_saddle_point() { let input = vec![vec![1, 2, 3], vec![3, 1, 2], vec![2, 3, 1]]; let expected: Vec<(usize, usize)> = Vec::new(); assert_eq!(expected, find_saddle_points(&input)); } #[test] //#[ignore] fn multiple_saddle_points_in_col() { let input = vec![vec![4, 5, 4], vec![3, 5, 5], vec![1, 5, 4]]; assert_eq!( vec![(0, 1), (1, 1), (2, 1)], find_sorted_saddle_points(&input) ); } #[test] //#[ignore] fn multiple_saddle_points_in_row() { let input = vec![vec![6, 7, 8], vec![5, 5, 5], vec![7, 5, 6]]; assert_eq!( vec![(1, 0), (1, 1), (1, 2)], find_sorted_saddle_points(&input) ); } #[test] //#[ignore] fn identify_bottom_right_saddle_point() { let input = vec![vec![8, 7, 9], vec![6, 7, 6], vec![3, 2, 5]]; assert_eq!(vec![(2, 2)], find_saddle_points(&input)); } // track specific as of v1.3 #[test] //#[ignore] fn non_square_matrix_high() { let input = vec![vec![1, 5], vec![3, 6], vec![2, 7], vec![3, 8]]; assert_eq!(vec![(0, 1)], find_saddle_points(&input)); } #[test] //#[ignore] fn non_square_matrix_wide() { let input = vec![vec![3, 1, 3], vec![3, 2, 4]]; assert_eq!(vec![(0, 0), (0, 2)], find_sorted_saddle_points(&input)); } #[test] //#[ignore] fn single_column_matrix() { let input = vec![vec![2], vec![1], vec![4], vec![1]]; assert_eq!(vec![(1, 0), (3, 0)], find_saddle_points(&input)); } #[test] //#[ignore] fn single_row_matrix() { let input = vec![vec![2, 5, 3, 5]]; assert_eq!(vec![(0, 1), (0, 3)], find_saddle_points(&input)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn find_saddle_points(input: &[Vec<u64>]) -> Vec<(usize, usize)> { let mut saddle_points = Vec::new(); let width = input.len(); let height = input[0].len(); for i in 0..width { for j in 0..height { let column = input.iter().map(|x| x[j]).collect::<Vec<u64>>(); let row = &input[i]; let max = row.iter().max().unwrap(); let min = column.iter().min().unwrap(); let value = input[i][j]; if value >= *max && value <= *min { saddle_points.push((i, j)); } } } saddle_points } #}
填充/相关
Isogram
1. Readme
等值线
确定是否一词,或短语是等值线(isograms
).
在等值线(又称为”无定形的 Word 是一个字或短语”)没有重复字母的单词或短语的逻辑术语,但允许连字符和空格出现多次。
等值线的例子:
- lumberjacks
- background
- downstream
- six-year-old
检测 isograms 单词,但它不是等值线,因为s
重复了.
资源
维基百科https://en.wikipedia.org/wiki/Isogram
2. 开始你的表演
pub fn check(candidate: &str) -> bool { unimplemented!("Is {} an isogram?", candidate); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn empty_string() { assert_eq!(check(""), true, "An empty string should be an isogram.") } #[test] //#[ignore] fn only_lower_case_characters() { assert_eq!(check("isogram"), true, "\"isogram\" should be an isogram.") } #[test] //#[ignore] fn one_duplicated_character() { assert_eq!( check("eleven"), false, "\"eleven\" has more than one \'e\', therefore it is no isogram." ) } #[test] //#[ignore] fn longest_reported_english_isogram() { assert_eq!( check("subdermatoglyphic"), true, "\"subdermatoglyphic\" should be an isogram." ) } #[test] //#[ignore] fn one_duplicated_character_mixed_case() { assert_eq!( check("Alphabet"), false, "\"Alphabet\" has more than one \'a\', therefore it is no isogram." ) } #[test] //#[ignore] fn hypothetical_isogramic_word_with_hyphen() { assert_eq!( check("thumbscrew-japingly"), true, "\"thumbscrew-japingly\" should be an isogram." ) } #[test] //#[ignore] fn isogram_with_duplicated_hyphen() { assert_eq!( check("six-year-old"), true, "\"six-year-old\" should be an isogram." ) } #[test] //#[ignore] fn made_up_name_that_is_an_isogram() { assert_eq!( check("Emily Jung Schwartzkopf"), true, "\"Emily Jung Schwartzkopf\" should be an isogram." ) } #[test] //#[ignore] fn duplicated_character_in_the_middle() { assert_eq!( check("accentor"), false, "\"accentor\" has more than one \'c\', therefore it is no isogram." ) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn check(word: &str) -> bool { // Filter all non-Alphabetic character out and collect them in a new String let normalized_string: String = word.to_lowercase() .chars() .filter(|c| c.is_alphabetic()) .collect(); /* Find the char element from back and front and compare the index. If it is the same unique char the index will be the same.*/ let is_unique = |x: char, word: &str| word.find(x).unwrap() == word.rfind(x).unwrap(); // Length should be the same if it is a isogram normalized_string.len() == normalized_string .chars() .filter(|&x| is_unique(x, normalized_string.as_str())) .count() } #}
填充/相关
Say
1. Readme
说
提供一个 0 到 999,999,999,999 之间的数字,用英语拼出这个数字.
步骤 1
处理 0 到 99 的基本情况.
如果程序的输入是22
,那么输出应该是'twenty-two'
.
如果给出超出规定范围的数字,您的程序应该大声抱怨。
这个程序的一些好的测试用例是:
0
14
50
98
-1
100
扩展
如果您使用的是 Mac,那就是 Mac OS X 的 shell 可以用say
程序大声说出来。如果您使用的是 Linux 或 Windows,则可以使用espeak
命令使用 eSpeakNG.
第 2 步
实现将数子分成块.
像1234567890
,应该产生如 1,234,567 和 890 的列表,而更简单1000
应该只产生 1 和 0.
该程序还必须报告超出范围的值。
第 3 步
现在处理在这些块之间,插入适当的缩放词.
所以1234567890
应该输出'1 billion 234 million 567 thousand 890'
该程序还必须报告超出范围的值。上限为”兆(trillion)”处就可以.
第 4 步
把它们放在一起,除了简单的英语之外什么都没有.
12345
应该给出twelve thousand three hundred forty-five
.
该程序还必须报告超出范围的值.
扩展
(正确地)使用英语and合并数字:
14
=> “fourteen”.100
=> “one hundred”.120
=> “one hundred and twenty”.1002
=> “one thousand and two”.1323
=> “one thousand three hundred and twenty-three”.
特定的 Rust 练习笔记
与本练习的其他语言版本相比,Rust 版本略有改变。我们使用 Rust 的强类型系统来限制输入,而不是要求您返回超出范围的错误。让函数处理所有有效输入要容易得多,而不是要求,使用模块的用户处理错误。
有一个 -1 版本的测试用例,但它被注释掉了。如果您的函数正确实现,则不应编译 -1 测试用例.
在测试用例中尚未实现,将”and”添加到数字文本中.
扩展
添加转换为 u64 的最大值的功能:9,223,372,036,854,775,807.
有关输出的提示,请查看最后一个测试用例.
资源
JavaRanch CattleDrive 的变体,练习 4ahttp://www.javaranch.com/say.jsp
2. 开始你的表演
pub fn encode(n: u64) -> String { unimplemented!("Say {} in English.", n); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // Note: No tests created using 'and' with numbers. // Apparently Most American English does not use the 'and' with numbers, // where it is common in British English to use the 'and'. #[test] fn test_zero() { assert_eq!(encode(0), String::from("zero")); } // // If the below test is uncommented, it should not compile. // /* #[test] //#[ignore] fn test_negative() { assert_eq!(encode(-1), String::from("won't compile")); } */ #[test] //#[ignore] fn test_one() { assert_eq!(encode(1), String::from("one")); } #[test] //#[ignore] fn test_fourteen() { assert_eq!(encode(14), String::from("fourteen")); } #[test] //#[ignore] fn test_twenty() { assert_eq!(encode(20), String::from("twenty")); } #[test] //#[ignore] fn test_twenty_two() { assert_eq!(encode(22), String::from("twenty-two")); } #[test] //#[ignore] fn test_one_hundred() { assert_eq!(encode(100), String::from("one hundred")); } // note, using American style with no and #[test] //#[ignore] fn test_one_hundred_twenty() { assert_eq!(encode(120), String::from("one hundred twenty")); } #[test] //#[ignore] fn test_one_hundred_twenty_three() { assert_eq!(encode(123), String::from("one hundred twenty-three")); } #[test] //#[ignore] fn test_one_thousand() { assert_eq!(encode(1000), String::from("one thousand")); } #[test] //#[ignore] fn test_one_thousand_two_hundred_thirty_four() { assert_eq!( encode(1234), String::from("one thousand two hundred thirty-four") ); } // note, using American style with no and #[test] //#[ignore] fn test_eight_hundred_and_ten_thousand() { assert_eq!(encode(810_000), String::from("eight hundred ten thousand")); } #[test] //#[ignore] fn test_one_million() { assert_eq!(encode(1_000_000), String::from("one million")); } // note, using American style with no and #[test] //#[ignore] fn test_one_million_two() { assert_eq!(encode(1_000_002), String::from("one million two")); } #[test] //#[ignore] fn test_1002345() { assert_eq!( encode(1_002_345), String::from("one million two thousand three hundred forty-five") ); } #[test] //#[ignore] fn test_one_billion() { assert_eq!(encode(1_000_000_000), String::from("one billion")); } #[test] //#[ignore] fn test_987654321123() { assert_eq!( encode(987_654_321_123), String::from( "nine hundred eighty-seven billion \ six hundred fifty-four million \ three hundred twenty-one thousand \ one hundred twenty-three" ) ); } /* These tests are only if you implemented full parsing for u64 type. */ #[test] //#[ignore] fn test_max_i64() { assert_eq!( encode(9_223_372_036_854_775_807), String::from( "nine quintillion two hundred twenty-three \ quadrillion three hundred seventy-two trillion \ thirty-six billion eight hundred fifty-four million \ seven hundred seventy-five thousand eight hundred seven" ) ); } #[test] //#[ignore] fn test_max_u64() { assert_eq!( encode(18_446_744_073_709_551_615), String::from( "eighteen quintillion four hundred forty-six \ quadrillion seven hundred forty-four trillion \ seventy-three billion seven hundred nine million \ five hundred fifty-one thousand six hundred fifteen" ) ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { const SMALL: &'static [&'static str] = &[ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", ]; const TENS: &'static [&'static str] = &[ "ones", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety", ]; const SCALE: &'static [&'static str] = &[ "", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", ]; pub fn encode(n: u64) -> String { if n < 20 { SMALL[n as usize].to_string() } else if n < 100 { let small = n % 10; let mut out = String::from(TENS[n as usize / 10]); if small > 0 { out.push('-'); out.push_str(SMALL[small as usize]); } out } else if n < 1000 { let mut out = String::from(SMALL[n as usize / 100]); out.push_str(" hundred"); let ones = n % 100; if ones > 0 { out.push(' '); out.push_str(&encode(ones)); } out } else { let mut sets: Vec<u64> = Vec::new(); let mut val = n; while val >= 1 { sets.push(val % 1000); val /= 1000; } let mut out = String::new(); while let Some(modu) = sets.pop() { let len = sets.len(); if modu == 0 { continue; } if out.len() > 0 { out.push(' '); } out.push_str(&encode(modu)); if len > 0 { out.push(' '); out.push_str(SCALE[len]); } } out } } #}
填充/相关
Run Length Encoding
1. Readme
运行游程编码
实现编码和解码.
游程编码(RLE)是一种简单的数据压缩形式,其中运行的(连续数据元素)仅由一个数据值和计数代替。
例如,我们可以只用 13 字节,就可以 代表原始的 53 个字符.
"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB"
RLE 允许从压缩数据中,完美地重建原始数据,这使其成为无损数据压缩.
"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE"
为简单起见,您可以假设未编码的字符串,仅包含字母 A 到 Z(小写或大写)和空格。这样,要编码的数据将永远 不包含 任何数字,并且要解码的数据内的数字始终表示后续字符的计数。
资源
维基百科https://en.wikipedia.org/wiki/Run-length_encoding
2. 开始你的表演
pub fn encode(source: &str) -> String { unimplemented!("Return the run-length encoding of {}.", source); } pub fn decode(source: &str) -> String { unimplemented!("Return the run-length decoding of {}.", source); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // encoding tests #[test] fn test_encode_empty_string() { assert_eq!("", encode("")); } #[test] //#[ignore] fn test_encode_single_characters() { assert_eq!("XYZ", encode("XYZ")); } #[test] //#[ignore] fn test_encode_string_with_no_single_characters() { assert_eq!("2A3B4C", encode("AABBBCCCC")); } #[test] //#[ignore] fn test_encode_single_characters_mixed_with_repeated_characters() { assert_eq!( "12WB12W3B24WB", encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB") ); } #[test] //#[ignore] fn test_encode_multiple_whitespace_mixed_in_string() { assert_eq!("2 hs2q q2w2 ", encode(" hsqq qww ")); } #[test] //#[ignore] fn test_encode_lowercase_characters() { assert_eq!("2a3b4c", encode("aabbbcccc")); } // decoding tests #[test] //#[ignore] fn test_decode_empty_string() { assert_eq!("", decode("")); } #[test] //#[ignore] fn test_decode_single_characters_only() { assert_eq!("XYZ", decode("XYZ")); } #[test] //#[ignore] fn test_decode_string_with_no_single_characters() { assert_eq!("AABBBCCCC", decode("2A3B4C")); } #[test] //#[ignore] fn test_decode_single_characters_with_repeated_characters() { assert_eq!( "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB", decode("12WB12W3B24WB") ); } #[test] //#[ignore] fn test_decode_multiple_whitespace_mixed_in_string() { assert_eq!(" hsqq qww ", decode("2 hs2q q2w2 ")); } #[test] //#[ignore] fn test_decode_lower_case_string() { assert_eq!("aabbbcccc", decode("2a3b4c")); } // consistency test #[test] //#[ignore] fn test_consistency() { assert_eq!("zzz ZZ zZ", decode(encode("zzz ZZ zZ").as_str())); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::cmp; pub fn encode(input: &str) -> String { input .chars() .fold( (String::new(), ' ', 0, 1), |(mut acc, last, last_n, pos), c| { // acc = where answer is accumulated // last = last character read // last_n = accum count for last if c == last { if pos == input.len() { // end of string acc += (last_n + 1).to_string().as_str(); acc.push(c); } (acc, last, last_n + 1, pos + 1) } else { if last_n > 1 { acc += last_n.to_string().as_str(); } if last_n > 0 { // ignore initial last (single whitespace) acc.push(last); } if pos == input.len() { // end of string acc.push(c); } (acc, c, 1, pos + 1) } }, ) .0 } pub fn decode(input: &str) -> String { input .chars() .fold((String::new(), 0), |(mut acc, last_n), c| { if let Some(d) = c.to_digit(10) { (acc, 10 * last_n + d) } else { acc += c.to_string().repeat(cmp::max(last_n, 1) as usize).as_str(); (acc, 0) } }) .0 } #}
填充/相关
ISBN Verifier
1. Readme
ISBN 检验器
这个ISBN-10 检测进程用于验证图书识别号。通常包含破折号,看起来像:3-598-21508-8
国际标准书号
ISBN-10 格式是 9 位数字(0 到 9)加上一个校验字符(一个数字或一个 X)。在校验字符为 X 的情况下,这表示值”10”。这些可以与连字符(不管有没有)通信,并且可以通过以下公式检查它们的有效性:
(x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0
如果结果是 0,那么它是一个有效的 ISBN-10,否则它是无效的.
例子
让我们用 ISBN-103-598-21508-8
. 我们把它插入到公式中,得到:
(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
由于结果是 0,这证明我们的 ISBN 是有效的.
任务
给定一个字符串,程序应该检查所提供的字符串是否是有效的 ISBN-10。为了实现这一点,需要在计算 ISBN 的校验位数之前,考虑字符串的预处理/解析.
该程序应该能够验证 ISBN-10 的破折号(不管有没有).
告诫
在某些语言中,从字符串转换为数字可能是棘手的。现在,甚至更棘手的是,ISBN-10 的校验位可能是”X”(表示”10”).例如3-598-21507-X
是一个有效的 ISBN-10.
奖金任务
-
从输入 ISBN-10 生成有效的 ISBN-13(并且可能用类似验证器再次验证它).
-
生成有效的 ISBN,甚至可能从给定的起始 ISBN 中生成.
资源
将一个字符串,转换成一个数字和一些基本的处理,利用一个可靠的真实世界的例子.https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation
2. 开始你的表演
/// Determines whether the supplied string is a valid ISBN number pub fn is_valid_isbn(isbn: &str) -> bool { unimplemented!("Is {:?} a valid ISBN number?", isbn); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_valid() { assert!(is_valid_isbn("3-598-21508-8")); } #[test] //#[ignore] fn test_invalid_check_digit() { assert!(!is_valid_isbn("3-598-21508-9")); } #[test] //#[ignore] fn test_valid_check_digit_of_10() { assert!(is_valid_isbn("3-598-21507-X")); } #[test] //#[ignore] fn test_invalid_character_as_check_digit() { assert!(!is_valid_isbn("3-598-21507-A")); } #[test] //#[ignore] fn test_invalid_character_in_isbn() { assert!(!is_valid_isbn("3-598-2K507-0")); } #[test] //#[ignore] #[allow(non_snake_case)] fn test_invalid_isbn_with_invalid_X() { assert!(!is_valid_isbn("3-598-2X507-9")); } #[test] //#[ignore] fn test_valid_isbn_without_dashes() { assert!(is_valid_isbn("3598215088")); } #[test] //#[ignore] #[allow(non_snake_case)] fn test_valid_isbn_without_dashes_and_X_as_check() { assert!(is_valid_isbn("359821507X")); } #[test] //#[ignore] fn test_invalid_isbn_without_dashes_and_no_check_digit() { assert!(!is_valid_isbn("359821507")); } #[test] //#[ignore] fn test_invalid_isbn_without_dashes_and_too_long() { assert!(!is_valid_isbn("3598215078X")); } #[test] //#[ignore] fn test_invalid_isbn_without_check_digit() { assert!(!is_valid_isbn("3-598-21507")); } #[test] //#[ignore] fn test_invalid_isbn_too_long() { assert!(!is_valid_isbn("3-598-21507-XX")); } #[test] //#[ignore] fn test_valid_digits_invalid_length() { assert!(!is_valid_isbn("35982150881")); } #[test] //#[ignore] fn test_special_characters() { assert!(!is_valid_isbn("!@#%!@")); } #[test] //#[ignore] #[allow(non_snake_case)] fn test_invalid_isbn_with_check_digit_X_instead_of_0() { assert!(!is_valid_isbn("3-598-21515-X")); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { /// An ISBN type #[derive(PartialEq, Eq)] enum IsbnType { Isbn10, Isbn13, } /// Checks if an 'X' is valid at the given position for the given ISBN type #[allow(non_snake_case)] fn is_X_valid(position: &usize, isbn_type: &IsbnType) -> bool { (isbn_type == &IsbnType::Isbn10 && position == &9) || (isbn_type == &IsbnType::Isbn13 && position == &12) } /// Checks if a '-' is valid at the given position for the given ISBN type fn is_dash_valid(position: &usize, isbn_type: &IsbnType) -> bool { isbn_type == &IsbnType::Isbn13 && (position == &1 || position == &5 || position == &11) } /// Determines whether the supplied string is a valid ISBN number pub fn is_valid_isbn(isbn: &str) -> bool { let isbn_type = match isbn.len() { 10 => IsbnType::Isbn10, 13 => IsbnType::Isbn13, _ => return false, }; let mut checksum = 0; let mut coefficient = 10; for (position, c) in isbn.char_indices() { let digit_value = match c { '0'...'9' => c.to_digit(10).unwrap(), 'X' if is_X_valid(&position, &isbn_type) => 10, '-' if is_dash_valid(&position, &isbn_type) => continue, _ => return false, }; checksum += coefficient * digit_value; coefficient -= 1; } checksum % 11 == 0 } #}
填充/相关
Perfect Numbers
1. Readme
完全数
根据 Nicomachus’(60-120CE)的自然数分类方案,确定一个数是- Perfect(完全), Abundant(过剩数), Deficient(亏数)
希腊数学家Nicomachus设计了一种自然数的分类方案,将每一个数识别归类为 perfect, abundant, or deficient ,方案基于aliquot sum(等值和)。 等值和定义为不包括数字本身的约数(可除出整数)的总和。例如,15 的等值和是(1+3+5)=9.
- Perfect(完全): aliquot sum = number
- 6 ,因为 (1 + 2 + 3) = 6
- 28 ,因为 (1 + 2 + 4 + 7 + 14) = 28
- Abundant(过剩数): aliquot sum > number
- 12 , 因为 (1 + 2 + 3 + 4 + 6) = 16
- 24 , 因为 (1 + 2 + 3 + 4 + 6 + 8 + 12) = 36
- Deficient(亏数): aliquot sum < number
- 8 , 因为 (1 + 2 + 4) = 7
- 素数 都是 deficient
实现一种方法来确定给定的数字是否为Perfect。 根据您的语言轨迹,您还可能需要实现一种方法来确定给定的数字是否为Abundant或Deficient.
资源
取自 Neal Ford 的函数思维第 2 章.http://shop.oreilly.com/product/0636920029687.do
2. 开始你的表演
#[derive(Debug, PartialEq, Eq)] pub enum Classification { Abundant, Perfect, Deficient, } pub fn classify(num: u64) -> Option<Classification> { unimplemented!("classify {}", num); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { macro_rules! tests { ($property_test_func:ident { $( $(#[$attr:meta])* $test_name:ident( $( $param:expr ),* ); )+ }) => { $( $(#[$attr])* #[test] fn $test_name() { $property_test_func($( $param ),* ) } )+ } } fn test_classification(num: u64, result: Classification) { assert_eq!(classify(num), Some(result)); } #[test] fn basic() { assert_eq!(classify(0), None); } tests! { test_classification { test_1(1, Classification::Deficient); test_2(2, Classification::Deficient); test_4(4, Classification::Deficient); test_6(6, Classification::Perfect); test_12(12, Classification::Abundant); test_28(28, Classification::Perfect); test_30(30, Classification::Abundant); test_32(32, Classification::Deficient); test_33550335(33550335, Classification::Abundant); test_33550336(33550336, Classification::Perfect); test_33550337(33550337, Classification::Deficient); } } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn classify(num: u64) -> Option<Classification> { if num == 0 { return None; } let sum: u64 = (1..num).filter(|i| num % i == 0).sum(); if sum == num { Some(Classification::Perfect) } else if sum < num { Some(Classification::Deficient) } else { Some(Classification::Abundant) } } #[derive(Debug, PartialEq, Eq)] pub enum Classification { Abundant, Perfect, Deficient, } #}
填充/相关
Clock
1. Readme
时钟(Clock)
实现一个处理没有日期的时间的 clock.
您应该可以添加和减去,分钟数.
代表相同时间的两个时钟应该彼此相等.
具有.to_string()
的 Rust Traits
你为 Clock 结构实现了.to_string()
吗?
如果是这样,请尝试为 Clock 实现Display trait(特征)
特征为各种类型的功能实现,提供了通用方法.
对于其他学习, 可以试试为 Clock 类型实现 String::from
?
资源
与 Erin Drummond 的配对会议https://twitter.com/ebdrummond
2. 开始你的表演
pub struct Clock; impl Clock { pub fn new(hours: i32, minutes: i32) -> Self { unimplemented!( "Construct a new Clock from {} hours and {} minutes", hours, minutes ); } pub fn add_minutes(self, minutes: i32) -> Self { unimplemented!("Add {} minutes to existing Clock time", minutes); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // // Clock Creation // #[test] fn test_on_the_hour() { assert_eq!(Clock::new(8, 0).to_string(), "08:00"); } #[test] //#[ignore] fn test_past_the_hour() { assert_eq!(Clock::new(11, 9).to_string(), "11:09"); } #[test] //#[ignore] fn test_midnight_is_zero_hours() { assert_eq!(Clock::new(24, 0).to_string(), "00:00"); } #[test] //#[ignore] fn test_hour_rolls_over() { assert_eq!(Clock::new(25, 0).to_string(), "01:00"); } #[test] //#[ignore] fn test_hour_rolls_over_continuously() { assert_eq!(Clock::new(100, 0).to_string(), "04:00"); } #[test] //#[ignore] fn test_sixty_minutes_is_next_hour() { assert_eq!(Clock::new(1, 60).to_string(), "02:00"); } #[test] //#[ignore] fn test_minutes_roll_over() { assert_eq!(Clock::new(0, 160).to_string(), "02:40"); } #[test] //#[ignore] fn test_minutes_roll_over_continuously() { assert_eq!(Clock::new(0, 1723).to_string(), "04:43"); } #[test] //#[ignore] fn test_hours_and_minutes_roll_over() { assert_eq!(Clock::new(25, 160).to_string(), "03:40"); } #[test] //#[ignore] fn test_hours_and_minutes_roll_over_continuously() { assert_eq!(Clock::new(201, 3001).to_string(), "11:01"); } #[test] //#[ignore] fn test_hours_and_minutes_roll_over_to_exactly_midnight() { assert_eq!(Clock::new(72, 8640).to_string(), "00:00"); } #[test] //#[ignore] fn test_negative_hour() { assert_eq!(Clock::new(-1, 15).to_string(), "23:15"); } #[test] //#[ignore] fn test_negative_hour_roll_over() { assert_eq!(Clock::new(-25, 00).to_string(), "23:00"); } #[test] //#[ignore] fn test_negative_hour_roll_over_continuously() { assert_eq!(Clock::new(-91, 00).to_string(), "05:00"); } #[test] //#[ignore] fn test_negative_minutes() { assert_eq!(Clock::new(1, -40).to_string(), "00:20"); } #[test] //#[ignore] fn test_negative_minutes_roll_over() { assert_eq!(Clock::new(1, -160).to_string(), "22:20"); } #[test] //#[ignore] fn test_negative_minutes_roll_over_continuously() { assert_eq!(Clock::new(1, -4820).to_string(), "16:40"); } #[test] //#[ignore] fn test_negative_hour_and_minutes_both_roll_over() { assert_eq!(Clock::new(-25, -160).to_string(), "20:20"); } #[test] //#[ignore] fn test_negative_hour_and_minutes_both_roll_over_continuously() { assert_eq!(Clock::new(-121, -5810).to_string(), "22:10"); } // // Clock Math // #[test] //#[ignore] fn test_add_minutes() { let clock = Clock::new(10, 0).add_minutes(3); assert_eq!(clock.to_string(), "10:03"); } #[test] //#[ignore] fn test_add_no_minutes() { let clock = Clock::new(6, 41).add_minutes(0); assert_eq!(clock.to_string(), "06:41"); } #[test] //#[ignore] fn test_add_to_next_hour() { let clock = Clock::new(0, 45).add_minutes(40); assert_eq!(clock.to_string(), "01:25"); } #[test] //#[ignore] fn test_add_more_than_one_hour() { let clock = Clock::new(10, 0).add_minutes(61); assert_eq!(clock.to_string(), "11:01"); } #[test] //#[ignore] fn test_add_more_than_two_hours_with_carry() { let clock = Clock::new(0, 45).add_minutes(160); assert_eq!(clock.to_string(), "03:25"); } #[test] //#[ignore] fn test_add_across_midnight() { let clock = Clock::new(23, 59).add_minutes(2); assert_eq!(clock.to_string(), "00:01"); } #[test] //#[ignore] fn test_add_more_than_one_day() { let clock = Clock::new(5, 32).add_minutes(1500); assert_eq!(clock.to_string(), "06:32"); } #[test] //#[ignore] fn test_add_more_than_two_days() { let clock = Clock::new(1, 1).add_minutes(3500); assert_eq!(clock.to_string(), "11:21"); } #[test] //#[ignore] fn test_subtract_minutes() { let clock = Clock::new(10, 3).add_minutes(-3); assert_eq!(clock.to_string(), "10:00"); } #[test] //#[ignore] fn test_subtract_to_previous_hour() { let clock = Clock::new(10, 3).add_minutes(-30); assert_eq!(clock.to_string(), "09:33"); } #[test] //#[ignore] fn test_subtract_more_than_an_hour() { let clock = Clock::new(10, 3).add_minutes(-70); assert_eq!(clock.to_string(), "08:53"); } #[test] //#[ignore] fn test_subtract_across_midnight() { let clock = Clock::new(0, 3).add_minutes(-4); assert_eq!(clock.to_string(), "23:59"); } #[test] //#[ignore] fn test_subtract_more_than_two_hours() { let clock = Clock::new(0, 0).add_minutes(-160); assert_eq!(clock.to_string(), "21:20"); } #[test] //#[ignore] fn test_subtract_more_than_two_hours_with_borrow() { let clock = Clock::new(6, 15).add_minutes(-160); assert_eq!(clock.to_string(), "03:35"); } #[test] //#[ignore] fn test_subtract_more_than_one_day() { let clock = Clock::new(5, 32).add_minutes(-1500); assert_eq!(clock.to_string(), "04:32"); } #[test] //#[ignore] fn test_subtract_mores_than_two_days() { let clock = Clock::new(2, 20).add_minutes(-3000); assert_eq!(clock.to_string(), "00:20"); } // // Test Equality // #[test] //#[ignore] fn test_compare_clocks_for_equality() { assert_eq!(Clock::new(15, 37), Clock::new(15, 37)); } #[test] //#[ignore] fn test_compare_clocks_a_minute_apart() { assert_ne!(Clock::new(15, 36), Clock::new(15, 37)); } #[test] //#[ignore] fn test_compare_clocks_an_hour_apart() { assert_ne!(Clock::new(14, 37), Clock::new(15, 37)); } #[test] //#[ignore] fn test_compare_clocks_with_hour_overflow() { assert_eq!(Clock::new(10, 37), Clock::new(34, 37)); } #[test] //#[ignore] fn test_compare_clocks_with_hour_overflow_by_several_days() { assert_eq!(Clock::new(3, 11), Clock::new(99, 11)); } #[test] //#[ignore] fn test_compare_clocks_with_negative_hour() { assert_eq!(Clock::new(22, 40), Clock::new(-2, 40)); } #[test] //#[ignore] fn test_compare_clocks_with_negative_hour_that_wraps() { assert_eq!(Clock::new(17, 3), Clock::new(-31, 3)); } #[test] //#[ignore] fn test_compare_clocks_with_negative_hour_that_wraps_multiple_times() { assert_eq!(Clock::new(13, 49), Clock::new(-83, 49)); } #[test] //#[ignore] fn test_compare_clocks_with_minutes_overflow() { assert_eq!(Clock::new(0, 1), Clock::new(0, 1441)); } #[test] //#[ignore] fn test_compare_clocks_with_minutes_overflow_by_several_days() { assert_eq!(Clock::new(2, 2), Clock::new(2, 4322)); } #[test] //#[ignore] fn test_compare_clocks_with_negative_minute() { assert_eq!(Clock::new(2, 40), Clock::new(3, -20)) } #[test] //#[ignore] fn test_compare_clocks_with_negative_minute_that_wraps() { assert_eq!(Clock::new(4, 10), Clock::new(5, -1490)) } #[test] //#[ignore] fn test_compare_clocks_with_negative_minute_that_wraps_multiple() { assert_eq!(Clock::new(6, 15), Clock::new(6, -4305)) } #[test] //#[ignore] fn test_compare_clocks_with_negative_hours_and_minutes() { assert_eq!(Clock::new(7, 32), Clock::new(-12, -268)) } #[test] //#[ignore] fn test_compare_clocks_with_negative_hours_and_minutes_that_wrap() { assert_eq!(Clock::new(18, 7), Clock::new(-54, -11513)) } #[test] //#[ignore] fn test_compare_full_clock_and_zeroed_clock() { assert_eq!(Clock::new(24, 0), Clock::new(0, 0)) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::fmt; #[derive(Eq, PartialEq, Debug)] pub struct Clock { minutes: i32, } impl fmt::Display for Clock { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let hours = self.minutes / 60; let mins = self.minutes % 60; write!(f, "{:02}:{:02}", hours, mins) } } impl Clock { pub fn new(hour: i32, minute: i32) -> Self { Clock::build(hour * 60 + minute) } fn build(minutes: i32) -> Self { let mut mins = minutes; while mins < 0 { mins += 1440; } Clock { minutes: mins % 1440, } } pub fn add_minutes(&mut self, minutes: i32) -> Self { Clock::build(self.minutes + minutes) } } #}
填充/相关
DOT DSL
1. Readme
DOT DSL
编写类似于 Graphviz 点语言的特定领域语言.
一个Domain Specific Language (DSL)是针对特定域优化的小语言.
比如说DOT 语言允许您编写图形的文本描述,然后通过Graphviz中其中一个图形工具(如dot
)转换为图像,一个简单的图形如下所示:
graph {
graph [bgcolor="yellow"]
a [color="red"]
b [color="blue"]
a -- b [color="green"]
}
把它放在一个example.dot
文件中,并运行dot example.dot -T png -o example.png
,就会创建一个图像example.png
,其中黄色背景上的绿线连接的红色和蓝色圆圈。
创建类似于点语言的 DSL.
生成器模式(Builder pattern)
本练习希望您通过使用builder pattern
模式,构建多个结构。简而言之,此模式允许您将包含大量参数的结构的构造函数,拆分为多个单独的函数。这种方法为您提供了实现紧凑,但高度灵活的结构构造和配置的方法。你可以在该页面上面阅读更多相关信息。
2. 开始你的表演
pub mod graph { pub struct Graph; impl Graph { pub fn new() -> Self { unimplemented!("Construct a new Graph struct."); } } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[macro_use] extern crate maplit; use graph::graph_items::edge::Edge; use graph::graph_items::node::Node; use graph::Graph; #[test] fn test_empty_graph() { let graph = Graph::new(); assert!(graph.nodes.is_empty()); assert!(graph.edges.is_empty()); assert!(graph.attrs.is_empty()); } #[test] //#[ignore] fn test_graph_with_one_node() { let nodes = vec![Node::new("a")]; let graph = Graph::new().with_nodes(&nodes); assert!(graph.edges.is_empty()); assert!(graph.attrs.is_empty()); assert_eq!(graph.nodes, vec![Node::new("a")]); } #[test] //#[ignore] fn test_graph_with_one_node_with_keywords() { let nodes = vec![Node::new("a").with_attrs(&[("color", "green")])]; let graph = Graph::new().with_nodes(&nodes); assert!(graph.edges.is_empty()); assert!(graph.attrs.is_empty()); assert_eq!( graph.nodes, vec![Node::new("a").with_attrs(&[("color", "green")])] ); } #[test] //#[ignore] fn test_graph_with_one_edge() { let edges = vec![Edge::new("a", "b")]; let graph = Graph::new().with_edges(&edges); assert!(graph.nodes.is_empty()); assert!(graph.attrs.is_empty()); assert_eq!(graph.edges, vec![Edge::new("a", "b")]); } #[test] //#[ignore] fn test_graph_with_one_attribute() { let graph = Graph::new().with_attrs(&[("foo", "1")]); let expected_attrs = hashmap!{ "foo".to_string() => "1".to_string(), }; assert!(graph.nodes.is_empty()); assert!(graph.edges.is_empty()); assert_eq!(graph.attrs, expected_attrs); } #[test] //#[ignore] fn test_graph_with_attributes() { let nodes = vec![ Node::new("a").with_attrs(&[("color", "green")]), Node::new("c"), Node::new("b").with_attrs(&[("label", "Beta!")]), ]; let edges = vec![ Edge::new("b", "c"), Edge::new("a", "b").with_attrs(&[("color", "blue")]), ]; let attrs = vec![("foo", "1"), ("title", "Testing Attrs"), ("bar", "true")]; let expected_attrs = hashmap! { "foo".to_string() => "1".to_string(), "title".to_string() => "Testing Attrs".to_string(), "bar".to_string() => "true".to_string(), }; let graph = Graph::new() .with_nodes(&nodes) .with_edges(&edges) .with_attrs(&attrs); assert_eq!( graph.nodes, vec![ Node::new("a").with_attrs(&[("color", "green")]), Node::new("c"), Node::new("b").with_attrs(&[("label", "Beta!")]), ] ); assert_eq!( graph.edges, vec![ Edge::new("b", "c"), Edge::new("a", "b").with_attrs(&[("color", "blue")]), ] ); assert_eq!(graph.attrs, expected_attrs); } #[test] //#[ignore] fn test_graph_stores_attributes() { let attributes = [("foo", "bar"), ("bat", "baz"), ("bim", "bef")]; let graph = Graph::new().with_nodes( &['a', 'b', 'c'] .iter() .enumerate() .map(|(i, n)| Node::new(&n.to_string()).with_attrs(&attributes[i..i + 1])) .collect::<Vec<_>>(), ); assert_eq!( graph .get_node("c") .expect("node must be stored") .get_attr("bim"), Some("bef") ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub mod graph { use std::collections::HashMap; use self::graph_items::edge::Edge; use self::graph_items::node::Node; pub struct Graph { pub nodes: Vec<Node>, pub edges: Vec<Edge>, pub attrs: HashMap<String, String>, } impl Graph { pub fn new() -> Self { Graph { nodes: vec![], edges: vec![], attrs: HashMap::new(), } } pub fn with_nodes(mut self, nodes: &[Node]) -> Self { self.nodes = nodes.clone().to_vec(); self } pub fn with_edges(mut self, edges: &[Edge]) -> Self { self.edges = edges.clone().to_vec(); self } pub fn with_attrs(mut self, attrs: &[(&'static str, &'static str)]) -> Self { attrs.iter().for_each(|&(name, value)| { self.attrs.insert(name.to_string(), value.to_string()); }); self } pub fn get_node(&self, name: &str) -> Option<&Node> { self.nodes.iter().filter(|n| n.name == name).nth(0) } } pub mod graph_items { pub mod edge { use std::collections::HashMap; #[derive(Clone, PartialEq, Debug)] pub struct Edge { src: String, dst: String, attrs: HashMap<String, String>, } impl Edge { pub fn new(src: &str, dst: &str) -> Self { Edge { src: src.to_string(), dst: dst.to_string(), attrs: HashMap::new(), } } pub fn with_attrs(mut self, attrs: &[(&'static str, &'static str)]) -> Self { attrs.iter().for_each(|&(name, value)| { self.attrs.insert(name.to_string(), value.to_string()); }); self } } } pub mod node { use std::collections::HashMap; #[derive(Clone, PartialEq, Debug)] pub struct Node { pub name: String, attrs: HashMap<String, String>, } impl Node { pub fn new(name: &str) -> Self { Node { name: name.to_string(), attrs: HashMap::new(), } } pub fn with_attrs(mut self, attrs: &[(&'static str, &'static str)]) -> Self { attrs.iter().for_each(|&(name, value)| { self.attrs.insert(name.to_string(), value.to_string()); }); self } pub fn get_attr(&self, name: &str) -> Option<&str> { self.attrs.get(name).map(|v| v.as_ref()) } } } } } #}
填充/相关
Hamming
1. Readme
汉明(Hamming)
给 2 个长度为 n 的 DNA 序列,求汉明距离差异为多少。
通过比较两条 DNA 链,并计算其中有多少核苷酸与其他序列中的同等核苷酸不同.
GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
^ ^ ^ ^ ^ ^^
这两条 DNA 链之间的 汉明距离为 7.
实现说明
汉明长度仅定义为等长的序列,因此尝试计算不同长度的序列之间的汉明 距离不应该起作用。这种情况的一般处理(例如,引发异常与返回特殊值)可能在语言之间有所不同.
资源
罗瑟琳的计算点突变问题http://rosalind.info/problems/hamm/
2. 开始你的表演
/// Return the Hamming distance between the strings, /// or None if the lengths are mismatched. pub fn hamming_distance(s1: &str, s2: &str) -> Option<usize> { unimplemented!("What is the Hamming Distance between {:?} and {:?}", s1, s2); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_no_difference_between_empty_strands() { assert_eq!(hamming_distance("", ""), Some(0)); } #[test] //#[ignore] fn test_no_difference_between_identical_strands() { assert_eq!(hamming_distance("GGACTGA", "GGACTGA"), Some(0)); } #[test] //#[ignore] fn test_complete_hamming_distance_in_small_strand() { assert_eq!(hamming_distance("ACT", "GGA"), Some(3)); } #[test] //#[ignore] fn test_small_hamming_distance_in_the_middle_somewhere() { assert_eq!(hamming_distance("GGACG", "GGTCG"), Some(1)); } #[test] //#[ignore] fn test_larger_distance() { assert_eq!(hamming_distance("ACCAGGG", "ACTATGG"), Some(2)); } #[test] //#[ignore] fn test_first_string_is_longer() { assert_eq!(hamming_distance("AAA", "AA"), None); } #[test] //#[ignore] fn test_second_string_is_longer() { assert_eq!(hamming_distance("A", "AA"), None); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn hamming_distance(a: &str, b: &str) -> Option<usize> { if a.len() != b.len() { return None; } Some(a.chars().zip(b.chars()).filter(|&(a, b)| a != b).count()) } #}
填充/相关
Simple Linked List
1. Readme
简单链表
编写一个使用元素(Elements
)和列表(List
)的简单链表实现.
链表是计算机科学中的一种基本数据结构,常用于其他数据结构的实现。它们在函数式编程语言(如 Clojure、Erlang 或 Haskell)中很普遍,但是在命令式语言(如 Ruby 或 Python)中很少见。
最简单的链表是单链表。列表中的每个元素,包含数据和一个”next”字段,指向元素列表中的下一个元素。
链接列表的这种变体通常用于表示序列,或推/取堆栈(也称为 LIFO 堆栈;后进先出)。
作为第一步,让我们创建一个包含范围(1..10)的单一链接列表,并提供反转链接列表,和转换为数组,或从数组转换的函数.
在使用内置链表的语言实现这一点时,实现自己的抽象数据类型.
实现提示
不要实现结构SimpleLinkedList
作为一个Vec
包装。 你应该,在堆上分配节点。
这可以实现为:
# #![allow(unused_variables)] #fn main() { pub struct SimpleLinkedList<T> { head: Option<Box<Node<T>>>, } #}
这个head
字段指向该链表的第一个元素(节点)。
这个实现也需要一个Node
结构,其具有以下字段:
# #![allow(unused_variables)] #fn main() { struct Node<T> { data: T, next: Option<Box<Node<T>>>, } #}
data
包含存储的数据,以及next
指向下节点(如果可用)或无.
为什么使用Option<Box<Node<T>>>
?而不仅用Option<Node<T>>
?
自己试试.您将得到以下错误.
| struct Node<T>
| ^^^^^^^^^^^^^^ recursive type has infinite size
...
| next: Option<Node<T>>,
| --------------------- recursive without indirection
这个错误在于,编译时,rust 必须知道下一个节点的大小。因next
会是递归的(”一个节点,有一个节点-有一个节点…”),编译器不知道要分配多少内存。相反,Box是一个具有定义大小的堆指针.
资源
受”Ruby”中面向对象设计模式的数据结构和算法的启发,即单链表.http://www.brpreiss.com/books/opus8/html/page96.html#SECTION004300000000000000000
2. 开始你的表演
pub struct SimpleLinkedList<T> { // Delete this field // dummy is needed to avoid unused parameter error during compilation dummy: ::std::marker::PhantomData<T>, } impl<T> SimpleLinkedList<T> { pub fn new() -> Self { unimplemented!() } pub fn len(&self) -> usize { unimplemented!() } pub fn push(&mut self, _element: T) { unimplemented!() } pub fn pop(&mut self) -> Option<T> { unimplemented!() } pub fn peek(&self) -> Option<&T> { unimplemented!() } } impl<T: Clone> SimpleLinkedList<T> { pub fn rev(&self) -> SimpleLinkedList<T> { unimplemented!() } } impl<'a, T: Clone> From<&'a [T]> for SimpleLinkedList<T> { fn from(_item: &[T]) -> Self { unimplemented!() } } impl<T> Into<Vec<T>> for SimpleLinkedList<T> { fn into(self) -> Vec<T> { unimplemented!() } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_new_list_is_empty() { let list: SimpleLinkedList<u32> = SimpleLinkedList::new(); assert_eq!(list.len(), 0, "list's length must be 0"); } #[test] //#[ignore] fn test_push_increments_length() { let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); list.push(1); assert_eq!(list.len(), 1, "list's length must be 1"); list.push(2); assert_eq!(list.len(), 2, "list's length must be 2"); } #[test] //#[ignore] fn test_pop_decrements_length() { let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); list.push(1); list.push(2); list.pop(); assert_eq!(list.len(), 1, "list's length must be 1"); list.pop(); assert_eq!(list.len(), 0, "list's length must be 0"); } #[test] //#[ignore] fn test_pop_returns_last_added_element() { let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); list.push(1); list.push(2); assert_eq!(list.pop(), Some(2), "Element must be 2"); assert_eq!(list.pop(), Some(1), "Element must be 1"); assert_eq!(list.pop(), None, "No element should be contained in list"); } #[test] //#[ignore] fn test_peek_returns_head_element() { let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); assert_eq!(list.peek(), None, "No element should be contained in list"); list.push(2); assert_eq!(list.peek(), Some(&2), "Element must be 2"); assert_eq!(list.peek(), Some(&2), "Element must be still 2"); } #[test] //#[ignore] fn test_from_slice() { let array = ["1", "2", "3", "4"]; let mut list = SimpleLinkedList::from(array.as_ref()); assert_eq!(list.pop(), Some("4")); assert_eq!(list.pop(), Some("3")); assert_eq!(list.pop(), Some("2")); assert_eq!(list.pop(), Some("1")); } #[test] //#[ignore] fn test_reverse() { let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); list.push(1); list.push(2); list.push(3); let mut rev_list = list.rev(); assert_eq!(rev_list.pop(), Some(1)); assert_eq!(rev_list.pop(), Some(2)); assert_eq!(rev_list.pop(), Some(3)); assert_eq!(rev_list.pop(), None); } #[test] //#[ignore] fn test_into_vector() { let mut v = Vec::new(); let mut s = SimpleLinkedList::new(); for i in 1..4 { v.push(i); s.push(i); } let s_as_vec: Vec<i32> = s.into(); assert_eq!(v, s_as_vec); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub struct SimpleLinkedList<T> { head: Option<Box<Node<T>>>, len: usize, } struct Node<T> { data: T, next: Option<Box<Node<T>>>, } impl<T> SimpleLinkedList<T> { pub fn new() -> Self { SimpleLinkedList { head: None, len: 0 } } pub fn len(&self) -> usize { self.len } pub fn push(&mut self, element: T) { let node = Box::new(Node::new(element, self.head.take())); self.head = Some(node); self.len += 1; } pub fn pop(&mut self) -> Option<T> { match self.len { 0 => None, _ => { self.len -= 1; self.head.take().map(|node| { let node = *node; self.head = node.next; node.data }) } } } pub fn peek(&self) -> Option<&T> { self.head.as_ref().map(|node| &node.data) } } impl<T: Clone> SimpleLinkedList<T> { pub fn rev(&self) -> SimpleLinkedList<T> { let mut rev_list = SimpleLinkedList::new(); let mut next = self.head.as_ref().map(|node| &**node); while let Some(node) = next { rev_list.push(node.data.clone()); next = node.next.as_ref().map(|node| &**node); } rev_list } } impl<'a, T: Clone> From<&'a [T]> for SimpleLinkedList<T> { fn from(item: &[T]) -> Self { let mut list = SimpleLinkedList::new(); for i in item { list.push(i.clone()); } list } } impl<T> Into<Vec<T>> for SimpleLinkedList<T> { fn into(mut self) -> Vec<T> { let mut vec = Vec::new(); while let Some(data) = self.pop() { vec.push(data); } vec.reverse(); vec } } impl<T> Node<T> { pub fn new(element: T, next: Option<Box<Node<T>>>) -> Self { Node { data: element, next, } } } #}
填充/相关
pascals-triangle
1. Readme
杨辉三角形
计算给定的行数的 杨辉三角形。
在 杨辉 三角形中, 可以看到,一个数由它头顶左右两个数相加得到的。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
# ... etc
资源
在数学世界的杨辉三角形http://mathworld.wolfram.com/PascalsTriangle.html
维基百科https://zh.wikipedia.org/wiki/杨辉三角形
2. 开始你的表演
pub struct PascalsTriangle; impl PascalsTriangle { pub fn new(row_count: u32) -> Self { unimplemented!("create Pascal's triangle with {} rows", row_count); } pub fn rows(&self) -> Vec<Vec<u32>> { unimplemented!(); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn no_rows() { let pt = PascalsTriangle::new(0); let expected: Vec<Vec<u32>> = Vec::new(); assert_eq!(expected, pt.rows()); } #[test] //#[ignore] fn one_row() { let pt = PascalsTriangle::new(1); let expected: Vec<Vec<u32>> = vec![vec![1]]; assert_eq!(expected, pt.rows()); } #[test] //#[ignore] fn two_rows() { let pt = PascalsTriangle::new(2); let expected: Vec<Vec<u32>> = vec![vec![1], vec![1, 1]]; assert_eq!(expected, pt.rows()); } #[test] //#[ignore] fn three_rows() { let pt = PascalsTriangle::new(3); let expected: Vec<Vec<u32>> = vec![vec![1], vec![1, 1], vec![1, 2, 1]]; assert_eq!(expected, pt.rows()); } #[test] //#[ignore] fn last_of_four_rows() { let pt = PascalsTriangle::new(4); let expected: Vec<u32> = vec![1, 3, 3, 1]; assert_eq!(Some(expected), pt.rows().pop()); } #[test] //#[ignore] fn five_rows() { let pt = PascalsTriangle::new(5); let expected: Vec<Vec<u32>> = vec![ vec![1], vec![1, 1], vec![1, 2, 1], vec![1, 3, 3, 1], vec![1, 4, 6, 4, 1], ]; assert_eq!(expected, pt.rows()); } #[test] //#[ignore] fn six_rows() { let pt = PascalsTriangle::new(6); let expected: Vec<Vec<u32>> = vec![ vec![1], vec![1, 1], vec![1, 2, 1], vec![1, 3, 3, 1], vec![1, 4, 6, 4, 1], vec![1, 5, 10, 10, 5, 1], ]; assert_eq!(expected, pt.rows()); } #[test] //#[ignore] fn seven_rows() { let pt = PascalsTriangle::new(7); let expected: Vec<Vec<u32>> = vec![ vec![1], vec![1, 1], vec![1, 2, 1], vec![1, 3, 3, 1], vec![1, 4, 6, 4, 1], vec![1, 5, 10, 10, 5, 1], vec![1, 6, 15, 20, 15, 6, 1], ]; assert_eq!(expected, pt.rows()); } #[test] //#[ignore] fn ten_rows() { let pt = PascalsTriangle::new(10); let expected: Vec<Vec<u32>> = vec![ vec![1], vec![1, 1], vec![1, 2, 1], vec![1, 3, 3, 1], vec![1, 4, 6, 4, 1], vec![1, 5, 10, 10, 5, 1], vec![1, 6, 15, 20, 15, 6, 1], vec![1, 7, 21, 35, 35, 21, 7, 1], vec![1, 8, 28, 56, 70, 56, 28, 8, 1], vec![1, 9, 36, 84, 126, 126, 84, 36, 9, 1], ]; assert_eq!(expected, pt.rows()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub struct PascalsTriangle { row_count: u32, } impl PascalsTriangle { pub fn new(row_count: u32) -> Self { PascalsTriangle { row_count: row_count, } } pub fn rows(&self) -> Vec<Vec<u32>> { (0..self.row_count) .map(|row| PascalsTriangle::row(row)) .collect() } pub fn row(number: u32) -> Vec<u32> { let mut r = vec![1]; for p in 1..(number + 1) { if let Some(&last) = r.last() { r.push((last * (number + 1 - p)) / p) } } r } } #}
填充/相关
Scrabble Score
1. Readme
拼字母的分数
给出一个单词,计算该单词的字母的分数.
字母价值
你需要这些:
26个英文字母 对应有多少分
A, E, I, O, U, L, N, R, S, T 1
D, G 2
B, C, M, P 3
F, H, V, W, Y 4
K 5
J, X 8
Q, Z 10
例子
“cabbage”的得分值应为 14 分:
- C , 就得 3 分
- A , 就得 1 分,两次
- B , 就得 3 分,两次
- G , 就得 2 分
- E , 就得 1 分
总计:
3 + 2*1 + 2*3 + 2 + 1
- =
3 + 2 + 6 + 3
- =
5 + 9
- = 14
扩展
- 您可以玩,双重或三重字母.
- 你可以玩,一个双重或三个单词.
资源
受到 Extreme Startup 游戏的启发https://github.com/rchatley/extreme_startup
2. 开始你的表演
/// Compute the Scrabble score for a word. pub fn score(word: &str) -> u64 { unimplemented!("Score {} in Scrabble.", word); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn a_is_worth_one_point() { assert_eq!(score("a"), 1); } #[test] //#[ignore] fn scoring_is_case_insensitive() { assert_eq!(score("A"), 1); } #[test] //#[ignore] fn f_is_worth_four() { assert_eq!(score("f"), 4); } #[test] //#[ignore] fn two_one_point_letters_make_a_two_point_word() { assert_eq!(score("at"), 2); } #[test] //#[ignore] fn three_letter_word() { assert_eq!(score("zoo"), 12); } #[test] //#[ignore] fn medium_word() { assert_eq!(score("street"), 6); } #[test] //#[ignore] fn longer_words_with_valuable_letters() { assert_eq!(score("quirky"), 22); } #[test] //#[ignore] fn long_mixed_case_word() { assert_eq!(score("OxyphenButazone"), 41); } #[test] //#[ignore] fn non_english_scrabble_letters_do_not_score() { assert_eq!(score("pinata"), 8, "'n' should score 1"); assert_eq!(score("piñata"), 7, "'ñ' should score 0"); } #[test] //#[ignore] fn empty_words_are_worth_zero() { assert_eq!(score(""), 0); } #[test] //#[ignore] fn all_letters_work() { assert_eq!(score("abcdefghijklmnopqrstuvwxyz"), 87); } #[test] //#[ignore] fn german_letters_do_not_score() { assert_eq!(score("STRASSE"), 7, "\"SS\" should score 2"); assert_eq!(score("STRAßE"), 5, "'ß' should score 0"); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; pub fn score(word: &str) -> u16 { let values = dictionary(); word.to_lowercase() .chars() .map(|c| values.get(&c).clone()) .fold(0, |score, v| score + v.unwrap_or(&0)) } fn dictionary() -> HashMap<char, u16> { let mut values = HashMap::new(); values.insert('a', 1); values.insert('b', 3); values.insert('c', 3); values.insert('d', 2); values.insert('e', 1); values.insert('f', 4); values.insert('g', 2); values.insert('h', 4); values.insert('i', 1); values.insert('j', 8); values.insert('k', 5); values.insert('l', 1); values.insert('m', 3); values.insert('n', 1); values.insert('o', 1); values.insert('p', 3); values.insert('q', 10); values.insert('r', 1); values.insert('s', 1); values.insert('t', 1); values.insert('u', 1); values.insert('v', 4); values.insert('w', 4); values.insert('x', 8); values.insert('y', 4); values.insert('z', 10); values } #}
填充/相关
Pangram
1. Readme
全字母句(pangram)
判断句子是否是全字母句。全字母句(希腊语:παγρμμα,pan 语法,”每个字母”)是一个使用字母表中每个字母,至少一次的句子。最著名的英语是 全字母句:
The quick brown fox jumps over the lazy dog.
字母表由 ASCII 字母a
到z
的全部组成,并且不区分大小写。输入不能包含非 ASCII 符号.
资源
维基百科https://en.wikipedia.org/wiki/Pangram
2. 开始你的表演
/// Determine whether a sentence is a pangram. pub fn is_pangram(sentence: &str) -> bool { unimplemented!("Is {} a pangram?", sentence); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn empty_strings_are_not_pangrams() { let sentence = ""; assert!(!is_pangram(&sentence)); } #[test] //#[ignore] fn classic_pangram_is_a_pangram() { let sentence = "the quick brown fox jumps over the lazy dog"; assert!(is_pangram(&sentence)); } #[test] //#[ignore] fn pangrams_must_have_all_letters() { let sentence = "a quick movement of the enemy will jeopardize five gunboats"; assert!(!is_pangram(&sentence)); } #[test] //#[ignore] fn pangrams_must_have_all_letters_two() { let sentence = "the quick brown fish jumps over the lazy dog"; assert!(!is_pangram(&sentence)); } #[test] //#[ignore] fn pangrams_must_include_z() { let sentence = "the quick brown fox jumps over the lay dog"; assert!(!is_pangram(&sentence)); } #[test] //#[ignore] fn underscores_do_not_affect_pangrams() { let sentence = "the_quick_brown_fox_jumps_over_the_lazy_dog"; assert!(is_pangram(&sentence)); } #[test] //#[ignore] fn numbers_do_not_affect_pangrams() { let sentence = "the 1 quick brown fox jumps over the 2 lazy dogs"; assert!(is_pangram(&sentence)); } #[test] //#[ignore] fn numbers_can_not_replace_letters() { let sentence = "7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"; assert!(!is_pangram(&sentence)); } #[test] //#[ignore] fn capitals_and_punctuation_can_be_in_pangrams() { let sentence = "\"Five quacking Zephyrs jolt my wax bed.\""; assert!(is_pangram(&sentence)); } #[test] //#[ignore] fn non_ascii_characters_can_be_in_pangrams() { let sentence = "Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich."; assert!(is_pangram(&sentence)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::BTreeSet; use std::iter::FromIterator; pub fn is_pangram(sentence: &str) -> bool { sentence .to_lowercase() .chars() .filter(|c| c.is_alphabetic()) .filter(|c| c.is_ascii()) .collect::<BTreeSet<char>>() == english_letter_set() } fn english_letter_set() -> BTreeSet<char> { BTreeSet::from_iter(ENGLISH_ALPHABET.chars()) } const ENGLISH_ALPHABET: &'static str = "abcdefghijklmnopqrstuvwxyz"; #}
填充/相关
Paasio
1. Readme
Paasio
报告网络 IO 统计.
你正在写一个PaaS,你需要一种给网络和文件系统使用的帐单.
为报告 IO 统计信息的网络连接和文件,创建包装器。包装器必须报告:
- 读取/写入的字节总数.
- 读取/写入操作的总数.
网络和文件的抽象
网络和文件操作,是实现了io::Read
和io::Write
traits
的。因此,有必要为您的类型实现这些特性.
资源
布瑞恩松岛https://github.com/bmatsuo
2. 开始你的表演
use std::io::{Read, Result, Write}; pub struct ReadStats<R>(::std::marker::PhantomData<R>); impl<R: Read> ReadStats<R> { // _wrapped is ignored because R is not bounded on Debug or Display and therefore // can't be passed through format!(). For actual implementation you will likely // wish to remove the leading underscore so the variable is not ignored. pub fn new(_wrapped: R) -> ReadStats<R> { unimplemented!() } pub fn get_ref(&self) -> &R { unimplemented!() } pub fn bytes_through(&self) -> usize { unimplemented!() } pub fn reads(&self) -> usize { unimplemented!() } } impl<R: Read> Read for ReadStats<R> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> { unimplemented!("Collect statistics about this call reading {:?}", buf) } } pub struct WriteStats<W>(::std::marker::PhantomData<W>); impl<W: Write> WriteStats<W> { // _wrapped is ignored because W is not bounded on Debug or Display and therefore // can't be passed through format!(). For actual implementation you will likely // wish to remove the leading underscore so the variable is not ignored. pub fn new(_wrapped: W) -> WriteStats<W> { unimplemented!() } pub fn get_ref(&self) -> &W { unimplemented!() } pub fn bytes_through(&self) -> usize { unimplemented!() } pub fn writes(&self) -> usize { unimplemented!() } } impl<W: Write> Write for WriteStats<W> { fn write(&mut self, buf: &[u8]) -> Result<usize> { unimplemented!("Collect statistics about this call writing {:?}", buf) } fn flush(&mut self) -> Result<()> { unimplemented!() } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { /// test a few read scenarios macro_rules! test_read { ($(#[$attr:meta])* $modname:ident ($input:expr, $len:expr)) => { mod $modname { use std::io::{Read, BufReader}; const CHUNK_SIZE: usize = 2; $(#[$attr])* #[test] fn test_read_passthrough() { let data = $input; let size = $len(&data); let mut reader = ReadStats::new(data); let mut buffer = Vec::with_capacity(size); let qty_read = reader.read_to_end(&mut buffer); assert!(qty_read.is_ok()); assert_eq!(size, qty_read.unwrap()); assert_eq!(size, buffer.len()); // 2: first to read all the data, second to check that // there wasn't any more pending data which simply didn't // fit into the existing buffer assert_eq!(2, reader.reads()); assert_eq!(size, reader.bytes_through()); } $(#[$attr])* #[test] fn test_read_chunks() { let data = $input; let size = $len(&data); let mut reader = ReadStats::new(data); let mut buffer = [0_u8; CHUNK_SIZE]; let mut chunks_read = 0; while reader.read(&mut buffer[..]).unwrap_or_else(|_| panic!("read failed at chunk {}", chunks_read+1)) > 0 { chunks_read += 1; } assert_eq!(size / CHUNK_SIZE, chunks_read); // we read once more than the number of chunks, because the final // read returns 0 new bytes assert_eq!(1+chunks_read, reader.reads()); assert_eq!(size, reader.bytes_through()); } $(#[$attr])* #[test] fn test_read_buffered_chunks() { let data = $input; let size = $len(&data); let mut reader = BufReader::new(ReadStats::new(data)); let mut buffer = [0_u8; CHUNK_SIZE]; let mut chunks_read = 0; while reader.read(&mut buffer[..]).unwrap_or_else(|_| panic!("read failed at chunk {}", chunks_read+1)) > 0 { chunks_read += 1; } assert_eq!(size / CHUNK_SIZE, chunks_read); // the BufReader should smooth out the reads, collecting into // a buffer and performing only two read operations: // the first collects everything into the buffer, // and the second ensures that no data remains assert_eq!(2, reader.get_ref().reads()); assert_eq!(size, reader.get_ref().bytes_through()); } } }; } /// test a few write scenarios macro_rules! test_write { ($(#[$attr:meta])* $modname:ident ($input:expr, $len:expr)) => { mod $modname { use std::io::{self, Write, BufWriter}; const CHUNK_SIZE: usize = 2; $(#[$attr])* #[test] fn test_write_passthrough() { let data = $input; let size = $len(&data); let mut writer = WriteStats::new(Vec::with_capacity(size)); let written = writer.write(data); assert!(written.is_ok()); assert_eq!(size, written.unwrap()); assert_eq!(size, writer.bytes_through()); assert_eq!(1, writer.writes()); assert_eq!(data, writer.get_ref().as_slice()); } $(#[$attr])* #[test] fn test_sink_oneshot() { let data = $input; let size = $len(&data); let mut writer = WriteStats::new(io::sink()); let written = writer.write(data); assert!(written.is_ok()); assert_eq!(size, written.unwrap()); assert_eq!(size, writer.bytes_through()); assert_eq!(1, writer.writes()); } $(#[$attr])* #[test] fn test_sink_windowed() { let data = $input; let size = $len(&data); let mut writer = WriteStats::new(io::sink()); let mut chunk_count = 0; for chunk in data.chunks(CHUNK_SIZE) { chunk_count += 1; let written = writer.write(chunk); assert!(written.is_ok()); assert_eq!(CHUNK_SIZE, written.unwrap()); } assert_eq!(size, writer.bytes_through()); assert_eq!(chunk_count, writer.writes()); } $(#[$attr])* #[test] fn test_sink_buffered_windowed() { let data = $input; let size = $len(&data); let mut writer = BufWriter::new(WriteStats::new(io::sink())); for chunk in data.chunks(CHUNK_SIZE) { let written = writer.write(chunk); assert!(written.is_ok()); assert_eq!(CHUNK_SIZE, written.unwrap()); } // at this point, nothing should have yet been passed through to // our writer assert_eq!(0, writer.get_ref().bytes_through()); assert_eq!(0, writer.get_ref().writes()); // after flushing, everything should pass through in one go assert!(writer.flush().is_ok()); assert_eq!(size, writer.get_ref().bytes_through()); assert_eq!(1, writer.get_ref().writes()); } } }; } #[test] fn test_create_stats() { let mut data: Vec<u8> = Vec::new(); let _ = ReadStats::new(data.as_slice()); let _ = WriteStats::new(data.as_mut_slice()); } test_read!( read_string ( "Twas brillig, and the slithy toves/Did gyre and gimble in the wabe:/All mimsy were the borogoves,/And the mome raths outgrabe.".as_bytes(), |d: &[u8]| d.len() )); test_write!( write_string ( "Beware the Jabberwock, my son!/The jaws that bite, the claws that catch!/Beware the Jubjub bird, and shun/The frumious Bandersnatch!".as_bytes(), |d: &[u8]| d.len() )); test_read!(read_byte_literal( &[1_u8, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144][..], |d: &[u8]| d.len() )); test_write!(write_byte_literal( &[2_u8, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,][..], |d: &[u8]| d.len() )); test_read!(read_file( ::std::fs::File::open("README.md").expect("readme must be present"), |f: &::std::fs::File| f.metadata().expect("metadata must be present").len() as usize )); #[test] fn read_stats_by_ref_returns_wrapped_reader() { use ReadStats; let input = "Why, sometimes I've believed as many as six impossible things before breakfast".as_bytes(); let reader = ReadStats::new(input); assert_eq!(reader.get_ref(), &input); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::io::{Read, Result, Write}; pub struct WriteStats<W: Write> { inner: W, bytes: usize, writes: usize, } impl<W: Write> WriteStats<W> { pub fn new(wrapped: W) -> WriteStats<W> { WriteStats { inner: wrapped, bytes: 0, writes: 0, } } pub fn get_ref(&self) -> &W { &self.inner } pub fn bytes_through(&self) -> usize { self.bytes } pub fn writes(&self) -> usize { self.writes } } impl<W: Write> Write for WriteStats<W> { fn write(&mut self, buf: &[u8]) -> Result<usize> { self.writes += 1; let written = self.inner.write(buf)?; self.bytes += written; Ok(written) } fn flush(&mut self) -> Result<()> { self.inner.flush() } } pub struct ReadStats<R: Read> { inner: R, bytes: usize, reads: usize, } impl<R: Read> ReadStats<R> { pub fn new(wrapped: R) -> ReadStats<R> { ReadStats { inner: wrapped, bytes: 0, reads: 0, } } pub fn get_ref(&self) -> &R { &self.inner } pub fn bytes_through(&self) -> usize { self.bytes } pub fn reads(&self) -> usize { self.reads } } impl<R: Read> Read for ReadStats<R> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> { self.reads += 1; let read = self.inner.read(buf)?; self.bytes += read; Ok(read) } } #}
填充/相关
Nucleotide Count
1. Readme
核苷酸(Nucleotide)计数
给定一条单 DNA 链 ,计算每个核苷酸在字符串中出现的次数.
地球上每一生物的遗传语言都是 DNA.DNA 是一种大分子,它是由一系列叫做核苷酸的单个元素组成。DNA 中存在 4 种类型,它们仅略有不同,并且可用以下符号表示:’A’表示腺嘌呤,’C’表示胞嘧啶,’G’表示鸟嘌呤,’T’表示胸腺嘧啶.
下面是一个类比:
- 树枝组成鸟的巢
- 核苷酸组成 DNA 链
资源
罗瑟琳 DNA 核苷酸问题的计算http://rosalind.info/problems/dna/
2. 开始你的表演
use std::collections::HashMap; pub fn count(nucleotide: char, dna: &str) -> Result<usize, char> { unimplemented!( "How much of nucleotide type '{}' is contained inside DNA string '{}'?", nucleotide, dna ); } pub fn nucleotide_counts(dna: &str) -> Result<HashMap<char, usize>, char> { unimplemented!( "How much of every nucleotide type is contained inside DNA string '{}'?", dna ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // use std::collections::HashMap; fn check_dna(s: &str, pairs: &[(char, usize)]) { // The reason for the awkward code in here is to ensure that the failure // message for assert_eq! is as informative as possible. A simpler // solution would simply check the length of the map, and then // check for the presence and value of each key in the given pairs vector. let mut m: HashMap<char, usize> = nucleotide_counts(s).unwrap(); for &(k, v) in pairs.iter() { assert_eq!((k, m.remove(&k)), (k, Some(v))); } // may fail with a message that clearly shows all extra pairs in the map assert_eq!(m.iter().collect::<Vec<(&char, &usize)>>(), vec![]); } #[test] fn count_returns_result() { assert!(count('A', "").is_ok()); } #[test] //#[ignore] fn test_count_empty() { assert_eq!(count('A', ""), Ok(0)); } #[test] //#[ignore] fn count_invalid_nucleotide() { assert_eq!(count('X', "A"), Err('X')); } #[test] //#[ignore] fn count_invalid_dna() { assert_eq!(count('A', "AX"), Err('X')); } #[test] //#[ignore] fn test_count_repetitive_cytosine() { assert_eq!(count('C', "CCCCC"), Ok(5)); } #[test] //#[ignore] fn test_count_only_thymine() { assert_eq!(count('T', "GGGGGTAACCCGG"), Ok(1)); } #[test] //#[ignore] fn counts_returns_result() { assert!(nucleotide_counts("ACGT").is_ok()); } #[test] //#[ignore] fn test_nucleotide_count_empty() { check_dna("", &[('A', 0), ('T', 0), ('C', 0), ('G', 0)]); } #[test] //#[ignore] fn test_nucleotide_count_only_guanine() { check_dna("GGGGGGGG", &[('A', 0), ('T', 0), ('C', 0), ('G', 8)]); } #[test] //#[ignore] fn test_nucleotide_count_counts_all() { check_dna( "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAA\ GAGTGTCTGATAGCAGC", &[('A', 20), ('T', 21), ('C', 12), ('G', 17)], ); } #[test] //#[ignore] fn counts_invalid_nucleotide_results_in_err() { assert_eq!(nucleotide_counts("GGXXX"), Err('X')); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; static VALID_NUCLEOTIDES: &'static str = "ACGT"; fn valid(c: char) -> Result<char, char> { if VALID_NUCLEOTIDES.contains(c) { Ok(c) } else { Err(c) } } pub fn count(nucleotide: char, input: &str) -> Result<usize, char> { valid(nucleotide)?; let mut count = 0; for c in input.chars() { if valid(c)? == nucleotide { count += 1; } } Ok(count) } pub fn nucleotide_counts(input: &str) -> Result<HashMap<char, usize>, char> { let mut map: HashMap<char, usize> = VALID_NUCLEOTIDES.chars().map(|c| (c, 0)).collect(); for nucleotide in input.chars() { if let Some(n) = map.get_mut(&nucleotide) { *n += 1; } else { return Err(nucleotide); } } Ok(map) } #}
填充/相关
Luhn
1. Readme
Luhn
给定一个数,判定它是否有效 Luhn 公式.
Luhn 算法,也称为“模 10”算法
这个Luhn 算法是一个简单的校验和公式,用于验证各种身份号码,如信用卡号码和加拿大社会保险号码.
任务是检查给定字符串是否有效.
验证一个数
长度为 1 或更小的字符串无效。在输入中允许使用空格,但在检查前,应清除空格。所有其他非数字字符都是不允许的.
例子 1:有效信用卡号码
4539 1488 0343 6467
LuHN 算法的第一步是,从右边开始每第二个数字加倍。我们要加倍的位
4_3_ 1_8_ 0_4_ 6_6_
如果加倍的数字值导致大于 9 的数字,则将此值减去 9。我们加倍的结果:
8569 2478 0383 3437
1_8_ => 2_7_
,8*2 - 9 = 7
然后把所有数字加起来:
8+5+6+9+2+4+7+8+0+3+8+3+3+4+3+7 = 80
如果总和可被 10 整除,则数字是有效的.这个号码是有效的!
例子 2:信用卡号码无效
8273 1232 7352 0569
把每第二个数字加倍,从右边开始
7253 2262 5312 0539
合计数字
7+2+5+3+2+2+6+2+5+3+1+2+0+5+3+9 = 57
57 不能被 10 整除,所以这个数字是无效的.
资源
维基百科上的 LuHn 算法http://en.wikipedia.org/wiki/Luhn_algorithm
2. 开始你的表演
/// Check a Luhn checksum. pub fn is_valid(code: &str) -> bool { unimplemented!("Is the Luhn checksum for {} valid?", code); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn single_digit_string_is_invalid() { assert!(!is_valid("1")); } #[test] //#[ignore] fn single_zero_string_is_invalid() { assert!(!is_valid("0")); } #[test] //#[ignore] fn simple_valid_sin() { assert!(is_valid(" 5 9 ")); } #[test] //#[ignore] fn valid_canadian_sin_is_valid() { assert!(is_valid("046 454 286")); } #[test] //#[ignore] fn invalid_canadian_sin_is_invalid() { assert!(!is_valid("046 454 287")); } #[test] //#[ignore] fn invalid_credit_card_is_invalid() { assert!(!is_valid("8273 1232 7352 0569")); } #[test] //#[ignore] fn strings_that_contain_non_digits_are_invalid() { assert!(!is_valid("046a 454 286")); } #[test] //#[ignore] fn punctuation_is_invalid() { assert!(!is_valid("055-444-285")); } #[test] //#[ignore] fn symbols_are_invalid() { assert!(!is_valid("055£ 444$ 285")); } #[test] //#[ignore] fn single_digit_with_space_is_invalid() { assert!(!is_valid(" 0")); } #[test] //#[ignore] fn lots_of_zeros_are_valid() { assert!(is_valid(" 00000")); } #[test] //#[ignore] fn another_valid_sin() { assert!(is_valid("055 444 285")); } #[test] //#[ignore] fn nine_doubled_is_nine() { assert!(is_valid("091")); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn is_valid(candidate: &str) -> bool { if candidate.chars().filter(|c| c.is_digit(10)).take(2).count() <= 1 || candidate.chars().any(|c| !c.is_digit(10) && c != ' ') { return false; } candidate .chars() .filter_map(|c| c.to_digit(10)) .rev() .enumerate() .map(|(index, digit)| if index % 2 == 0 { digit } else { digit * 2 }) .map(|digit| if digit > 9 { digit - 9 } else { digit }) .sum::<u32>() % 10 == 0 } #}
填充/相关
Largest Series Product
1. Readme
最大系列乘积
给定一个数字串,计算长度为 n 的连续子串的最大乘积.
例如,对于输入'1027839564'
, 3 位数系列的最大乘积是 270 (9 * 5 * 6
), 5 位数系列的最大乘积为 7560 (7 * 8 * 3 * 9 * 5
).
注意这些系列数字字符,在输入中,只要求相邻位置,不需要连续数值(123456..).
对于输入'73167176531330624919225119674426574742355349194934'
一系列 6 位数的最大乘积是 23520.
Rust 的最大系列乘积
这些迭代器可能是有用的,取决于您的方法.
资源
欧拉工程的问题 8 的一个变种http://projecteuler.net/problem=8
2. 开始你的表演
#[derive(Debug, PartialEq)] pub enum Error { SpanTooLong, InvalidDigit(char), } pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> { unimplemented!( "largest series product of a span of {} digits in {}", span, string_digits ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn return_is_a_result() { assert!(lsp("29", 2).is_ok()); } #[test] //#[ignore] fn find_the_largest_product_when_span_equals_length() { assert_eq!(Ok(18), lsp("29", 2)); } #[test] //#[ignore] fn find_the_largest_product_of_two_with_numbers_in_order() { assert_eq!(Ok(72), lsp("0123456789", 2)); } #[test] //#[ignore] fn find_the_largest_product_of_two_with_numbers_not_in_order() { assert_eq!(Ok(48), lsp("576802143", 2)); } #[test] //#[ignore] fn find_the_largest_product_of_three_with_numbers_in_order() { assert_eq!(Ok(504), lsp("0123456789", 3)); } #[test] //#[ignore] fn find_the_largest_product_of_three_with_numbers_not_in_order() { assert_eq!(Ok(270), lsp("1027839564", 3)); } #[test] //#[ignore] fn find_the_largest_product_of_five_with_numbers_in_order() { assert_eq!(Ok(15120), lsp("0123456789", 5)); } #[test] //#[ignore] fn span_of_six_in_a_large_number() { assert_eq!( Ok(23520), lsp("73167176531330624919225119674426574742355349194934", 6) ); } #[test] //#[ignore] fn returns_zero_if_number_is_zeros() { assert_eq!(Ok(0), lsp("0000", 2)); } #[test] //#[ignore] fn returns_zero_if_all_products_are_zero() { assert_eq!(Ok(0), lsp("99099", 3)); } #[test] //#[ignore] fn a_span_is_longer_than_number_is_an_error() { assert_eq!(Err(Error::SpanTooLong), lsp("123", 4)); } // There may be some confusion about whether this should be 1 or error. // The reasoning for it being 1 is this: // There is one 0-character string contained in the empty string. // That's the empty string itself. // The empty product is 1 (the identity for multiplication). // Therefore LSP('', 0) is 1. // It's NOT the case that LSP('', 0) takes max of an empty list. // So there is no error. // Compare against LSP('123', 4): // There are zero 4-character strings in '123'. // So LSP('123', 4) really DOES take the max of an empty list. // So LSP('123', 4) errors and LSP('', 0) does NOT. #[test] //#[ignore] fn an_empty_string_and_no_span_returns_one() { assert_eq!(Ok(1), lsp("", 0)); } #[test] //#[ignore] fn a_non_empty_string_and_no_span_returns_one() { assert_eq!(Ok(1), lsp("123", 0)); } #[test] //#[ignore] fn empty_string_and_non_zero_span_is_an_error() { assert_eq!(Err(Error::SpanTooLong), lsp("", 1)); } #[test] //#[ignore] fn a_string_with_non_digits_is_an_error() { assert_eq!(Err(Error::InvalidDigit('a')), lsp("1234a5", 2)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(Debug, PartialEq)] pub enum Error { SpanTooLong, InvalidDigit(char), } pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> { if span == 0 { return Ok(1); } if let Some(invalid) = string_digits.chars().find(|c| !c.is_digit(10)) { return Err(Error::InvalidDigit(invalid)); } let products: Vec<u64> = string_digits .chars() .map(|c| c.to_digit(10).unwrap() as u64) .collect::<Vec<u64>>() .windows(span) .map(|w| w.into_iter().product()) .collect(); if let Some(&x) = products.iter().max() { Ok(x) } else { Err(Error::SpanTooLong) } } #}
填充/相关
Word Count
1. Readme
字数
给定一个短语,计算该短语中,每个单词的出现次数.
例如输入"olly olly in come free"
olly: 2
in: 1
come: 1
free: 1
资源
这是一个经典的玩具问题,但我们通过在 Go Tour 中,看到它,才想起提醒.
2. 开始你的表演
use std::collections::HashMap; /// Count occurrences of words. pub fn word_count(words: &str) -> HashMap<String, u32> { unimplemented!("Count of occurrences of words in {:?}", words); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // use std::collections::HashMap; fn check_word_count(s: &str, pairs: &[(&str, u32)]) { // The reason for the awkward code in here is to ensure that the failure // message for assert_eq! is as informative as possible. A simpler // solution would simply check the length of the map, and then // check for the presence and value of each key in the given pairs vector. let mut m: HashMap<String, u32> = word_count(s); for &(k, v) in pairs.iter() { assert_eq!((k, m.remove(&k.to_string()).unwrap_or(0)), (k, v)); } // may fail with a message that clearly shows all extra pairs in the map assert_eq!(m.iter().collect::<Vec<(&String, &u32)>>(), vec![]); } #[test] fn test_count_one_word() { check_word_count("word", &[("word", 1)]); } #[test] //#[ignore] fn test_count_one_of_each() { check_word_count("one of each", &[("one", 1), ("of", 1), ("each", 1)]); } #[test] //#[ignore] fn test_count_multiple_occurrences() { check_word_count( "one fish two fish red fish blue fish", &[("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)], ); } #[test] //#[ignore] fn test_ignore_punctuation() { check_word_count( "car : carpet as java : javascript!!&@$%^&", &[ ("car", 1), ("carpet", 1), ("as", 1), ("java", 1), ("javascript", 1), ], ); } #[test] //#[ignore] fn test_include_numbers() { check_word_count( "testing, 1, 2 testing", &[("testing", 2), ("1", 1), ("2", 1)], ); } #[test] //#[ignore] fn test_normalize_case() { check_word_count("go Go GO Stop stop", &[("go", 3), ("stop", 2)]); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; pub fn word_count(input: &str) -> HashMap<String, u32> { let mut map: HashMap<String, u32> = HashMap::new(); let lower = input.to_lowercase(); let slice: &str = lower.as_ref(); for word in slice .split(|c: char| !c.is_alphanumeric()) .filter(|s| !s.is_empty()) { *map.entry(word.to_string()).or_insert(0) += 1; } map } #}
填充/相关
Atbash Cipher
1. Readme
Atbash Cipher
创建 Atbash 密码的实现,这是在中东创建的古老加密系统.
Atbash 密码是一种简单的替换密码,它依赖于转置字母表中的所有字母,使得生成的字母表向后。
- 第一个字母替换为最后一个字母,
- 第二个字母替换为倒数第二个字母,
- 依此类推.
拉丁字母的 Atbash 密码如下:
明文: abcdefghijklmnopqrstuvwxyz
加密: zyxwvutsrqponmlkjihgfedcba
它是一个非常弱的加密方式,因为它只有一个加密可能性,且为一个简单的单字母替换密码。但是,这并不是现在’加密游戏-练习时间’的问题.
密文以固定长度的组写出,传统的组大小为 5 个字母,并且不包括标点符号。这是为了使单词边界,更难猜测。
例子
test
,编码为gvhg
gvhg
,解码为test
gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt
解码为thequickbrownfoxjumpsoverthelazydog
资源
维基百科http://en.wikipedia.org/wiki/Atbash
2. 开始你的表演
/// "Encipher" with the Atbash cipher. pub fn encode(plain: &str) -> String { unimplemented!("Encoding of {:?} in Atbash cipher.", plain); } /// "Decipher" with the Atbash cipher. pub fn decode(cipher: &str) -> String { unimplemented!("Decoding of {:?} in Atbash cipher.", cipher); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_encode_yes() { assert_eq!("bvh", encode("yes")); } #[test] //#[ignore] fn test_encode_no() { assert_eq!("ml", encode("no")); } #[test] //#[ignore] fn test_encode_omg() { assert_eq!("lnt", encode("OMG")); } #[test] //#[ignore] fn test_encode_spaces() { assert_eq!("lnt", encode("O M G")); } #[test] //#[ignore] fn test_encode_mindblowingly() { assert_eq!("nrmwy oldrm tob", encode("mindblowingly")); } #[test] //#[ignore] fn test_encode_numbers() { assert_eq!("gvhgr mt123 gvhgr mt", encode("Testing,1 2 3, testing.")); } #[test] //#[ignore] fn test_encode_deep_thought() { assert_eq!("gifgs rhurx grlm", encode("Truth is fiction.")); } #[test] //#[ignore] fn test_encode_all_the_letters() { assert_eq!( "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt", encode("The quick brown fox jumps over the lazy dog.") ); } #[test] //#[ignore] fn test_encode_ignores_non_ascii() { assert_eq!("mlmzh xrrrt mlivw", encode("non ascii éignored")); } #[test] //#[ignore] fn test_decode_exercism() { assert_eq!("exercism", decode("vcvix rhn")); } #[test] //#[ignore] fn test_decode_a_sentence() { assert_eq!( "anobstacleisoftenasteppingstone", decode("zmlyh gzxov rhlug vmzhg vkkrm thglm v") ); } #[test] //#[ignore] fn test_decode_numbers() { assert_eq!("testing123testing", decode("gvhgr mt123 gvhgr mt")); } #[test] //#[ignore] fn test_decode_all_the_letters() { assert_eq!( "thequickbrownfoxjumpsoverthelazydog", decode("gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt") ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { fn ascii(ch: char) -> u8 { ch as u8 } fn get_transpose(ch: char) -> char { if ch.is_digit(10) { ch } else { (ascii('z') - ascii(ch) + ascii('a')) as char } } pub fn encode(plaintext: &str) -> String { plaintext .to_lowercase() .chars() .filter(|&ch| ch.is_ascii()) .filter(|&ch| ch.is_alphanumeric()) .map(|ch| get_transpose(ch)) .collect::<Vec<char>>() .chunks(5) .map(|slice| slice.iter().cloned().collect::<String>()) .collect::<Vec<String>>() .join(" ") } pub fn decode(ciphertext: &str) -> String { ciphertext .split::<char>(' ') .collect::<String>() .chars() .map(|ch| get_transpose(ch)) .collect::<String>() } #}
填充/相关
Crypto Square
1. Readme
加密广场
实现,用于组成称为方形代码的加密信息的经典方法.
给定英文文本,输出该文本的加密编码版本。
首先,输入被规范化:
- 从英文文本中删除空格和标点符号,并且消息是朝下的.
然后,
- 规范化字符被分成行。当使用插入的换行符打印时,这些行自自然然形成类似矩形的样子。
例如,句子
"If man was meant to stay on the ground, god would have given us roots."
规范化为:
"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots"
明文应该组织成一个矩形。矩形的大小(r x c
)应该根据消息的长度来决定c >= r
和c - r <= 1
,这里的c
是列数和r
是行数.
我们的标准化文本长度为 54 个字符,用c = 8
和r = 7
指示矩形:
"ifmanwas"
"meanttos"
"tayonthe"
"groundgo"
"dwouldha"
"vegivenu"
"sroots "
通过向下(第一行第一个,拼接第二行第一个),读取从左到右的列来获得编码消息.
上面的消息编码为:
"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"
根据输出的,矩形块编码文本的大小(r X c)
,表明有c
块r
长度的编码字串,以空格分隔。对于那些n
位字符,但少于规定的长度的,每个尾添一个空格。
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
请注意,如果我们要堆叠这些,我们可以直观地,将密文解码回原始消息: (第一行第一个,拼接第二行第一个...)
"imtgdvs"
"fearwer"
"mayoogo"
"anouuio"
"ntnnlvt"
"wttddes"
"aohghn "
"sseoau "
资源
J Dalbey 的编程实践问题http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html
2. 开始你的表演
pub fn encrypt(input: &str) -> String { unimplemented!("Encrypt {:?} using a square code", input) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn test(input: &str, output: &str) { assert_eq!(&encrypt(input), output); } #[test] fn test_empty_input() { test("", "") } #[test] //#[ignore] fn test_encrypt_also_decrypts_square() { // note that you only get the exact input back if: // 1. no punctuation // 2. even spacing // 3. all lowercase // 4. square input let example = "lime anda coco anut"; assert_eq!(example, &encrypt(&encrypt(example))); } #[test] //#[ignore] fn test_example() { test( "If man was meant to stay on the ground, god would have given us roots.", "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau ", ) } #[test] //#[ignore] fn test_empty_last_line() { test("congratulate", "crl oaa ntt gue") } #[test] //#[ignore] fn test_spaces_are_reorganized() { test("abet", "ae bt"); test("a bet", "ae bt"); test(" a b e t ", "ae bt"); } #[test] //#[ignore] fn test_everything_becomes_lowercase() { test("caSe", "cs ae"); test("cAsE", "cs ae"); test("CASE", "cs ae"); } #[test] //#[ignore] fn test_long() { test( r#" We choose to go to the moon. We choose to go to the moon in this decade and do the other things, not because they are easy, but because they are hard, because that goal will serve to organize and measure the best of our energies and skills, because that challenge is one that we are willing to accept, one we are unwilling to postpone, and one which we intend to win, and the others, too. -- John F. Kennedy, 12 September 1962 "#, &(String::from("womdbudlmecsgwdwob enooetbsenaotioihe ") + "cwotcbeeaeunolnnnr henhaecrsrsealeaf1 ocieucavugetciwnk9 " + "ohnosauerithcnhde6 sotteusteehaegitn2 eohhtseotsatptchn " + "tsiehetohatwtohee oesrethrenceopwod gtdtyhagbdhanoety " + "ooehaetaesaresih1 tgcirygnsklewtne2 ooaneaoitilweptrs " + "ttdgerazoleiaoese hoesaeleflnlrnntp etanshwaosgleedot " + "mhnoyainubeiuatoe oedtbrldreinnnojm "), ) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { extern crate itertools; use itertools::Itertools; /// Encrypt the input string using square cryptography pub fn encrypt(input: &str) -> String { let prepared = prepare(input); if prepared.len() == 0 { return String::new(); } let (cols, rows) = dimensions(prepared.len()); let mut output = String::with_capacity(input.len()); for chunk_iterator in SquareIndexer::new(rows, cols).chunks(cols).into_iter() { for ch_idx in chunk_iterator { if ch_idx < prepared.len() { output.push(prepared[ch_idx]); } } output.push(' '); } // we know there's one extra space at the end output.pop(); output } /// Construct a vector of characters from the given input. /// /// Constrain it to the allowed chars: lowercase ascii letters. /// We construct a vector here because the length of the input /// matters when constructing the output, so we need to know /// how many input chars there are. We could treat it as a stream /// and just stream twice, but collecting it into a vector works /// equally well and might be a bit faster. fn prepare(input: &str) -> Vec<char> { let mut output = Vec::with_capacity(input.len()); output.extend( input .chars() .filter(|&c| c.is_ascii() && !c.is_whitespace() && !c.is_ascii_punctuation()) .map(|c| c.to_ascii_lowercase()), ); // add space padding to the end such that the actual string returned // forms a perfect rectangle let (r, c) = dimensions(output.len()); let padding_qty = (r * c) - output.len(); for _ in 0..padding_qty { output.push(' '); } output.shrink_to_fit(); output } /// Get the dimensions of the appropriate bounding rectangle for this encryption /// /// To find `(rows, cols)` such that `cols >= rows && cols - rows <= 1`, we find /// the least square greater than or equal to the message length. Its square root /// is the cols. If the message length is a perfect square, `rows` is the same. /// Otherwise, it is one less. fn dimensions(length: usize) -> (usize, usize) { let cols = (length as f64).sqrt().ceil() as usize; let rows = if cols * cols == length { cols } else { cols - 1 }; (rows, cols) } /// Iterator over the indices of the appropriate chars of the output. /// /// For a (2, 3) (r, c) grid, yields (0, 3, 1, 4, 2, 5). /// Does no bounds checking or space insertion: that's handled elsewhere. #[derive(Debug)] struct SquareIndexer { rows: usize, cols: usize, cur_row: usize, cur_col: usize, max_value: usize, } impl SquareIndexer { fn new(rows: usize, cols: usize) -> SquareIndexer { SquareIndexer { rows: rows, cols: cols, cur_row: 0, cur_col: 0, max_value: rows * cols, } } } impl Iterator for SquareIndexer { type Item = usize; fn next(&mut self) -> Option<usize> { let value = self.cur_row + (self.cur_col * self.rows); let output = if value < self.max_value && self.cur_row < self.rows { Some(value) } else { None }; // now increment internal state to next value self.cur_col += 1; if self.cur_col >= self.cols { self.cur_col = 0; self.cur_row += 1; } output } } #}
填充/相关
Rotational Cipher
1. Readme
旋转密码
创建旋转密码的实现,有时也称为 Caesar 密码.
Caesar 密码是一个简单的移位密码,它依赖于使用0
到26
整数(key),在字母表中转置所有字母。由于模运算,使用0
要么26
,总是会产生相同的输出。将字母移动为与 key 值一样多的值。
旋转密码的一般表示法是ROT + <key>
。最常用的旋转密码是ROT13
.
一个拉丁字母表的ROT13
加密如下:
原文: abcdefghijklmnopqrstuvwxyz
密文: nopqrstuvwxyzabcdefghijklm
它比 Atbash 密码更强大,因为它有 27 个可能性 key,和 25 个可用的密文.
密文会与输入相同的格式写出,包括空格和标点符号.
例子
- ROT5
omg
给trl
- ROT0
c
给c
- ROT26
Cool
给Cool
- ROT13
The quick brown fox jumps over the lazy dog.
给Gur dhvpx oebja sbk whzcf bire gur ynml qbt.
- ROT13
Gur dhvpx oebja sbk whzcf bire gur ynml qbt.
给The quick brown fox jumps over the lazy dog.
资源
维基百科https://en.wikipedia.org/wiki/Caesar_cipher
2. 开始你的表演
pub fn rotate(input: &str, key: i8) -> String { unimplemented!( "How would input text '{}' transform when every letter is shifted using key '{}'?", input, key ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn rotate_a_1() { assert_eq!("b", rotate("a", 1)); } #[test] //#[ignore] fn rotate_a_26() { assert_eq!("a", rotate("a", 26)); } #[test] //#[ignore] fn rotate_a_0() { assert_eq!("a", rotate("a", 0)); } #[test] //#[ignore] fn rotate_m_13() { assert_eq!("z", rotate("m", 13)); } #[test] //#[ignore] fn rotate_n_13_with_wrap() { assert_eq!("a", rotate("n", 13)); } #[test] //#[ignore] fn rotate_caps() { assert_eq!("TRL", rotate("OMG", 5)); } #[test] //#[ignore] fn rotate_spaces() { assert_eq!("T R L", rotate("O M G", 5)); } #[test] //#[ignore] fn rotate_numbers() { assert_eq!("Xiwxmrk 1 2 3 xiwxmrk", rotate("Testing 1 2 3 testing", 4)); } #[test] //#[ignore] fn rotate_punctuation() { assert_eq!("Gzo\'n zvo, Bmviyhv!", rotate("Let\'s eat, Grandma!", 21)); } #[test] //#[ignore] fn rotate_all_the_letters() { assert_eq!( "Gur dhvpx oebja sbk whzcf bire gur ynml qbt.", rotate("The quick brown fox jumps over the lazy dog.", 13) ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn rotate(text: &str, shift_key: u8) -> String { text.chars() .map(|c| { let case = if c.is_uppercase() { 'A' } else { 'a' } as u8; if c.is_alphabetic() { (((c as u8 - case + shift_key) % 26) + case) as char } else { c } }) .collect::<String>() } #}
填充/相关
Simple Cipher
1. Readme
简单密码
实现一个简单的移位密码,像 Caesar 和,一个更安全的替换密码.
步骤 1
“如果他有什么秘密要说的话,他就是用密码写的,也就是说,通过改变字母表的字母顺序,一个字也说不出来。如果有人想破译这些,并理解它们的意思,他必须用字母表中的第四个字母,即 D,代替 A,以及其他的字母。 —Suetonius, Life of Julius Caesar
密码是非常直截了当的算法,使文本不可读,同时仍容易允许破译。他们容易受到许多形式的密码分析,但我们幸运的是,我们的小姐妹通常不是密码学家。
用 Caesar密码,加密来自 Julius Caesar 的消息。现在 Caesar 知道加密方式不是很好,但他还是有个好处: 几乎没有人能读对。所以对于一对夫妇的信件已经足够了,让人们无法识别他们所知道的几个字。
你的任务是创建一个简单的移位密码,就像 Caesar 密码一样。这个图像是 Caesar 密码的一个很好的例子:
例如:
将”iamapandabear”作为输入到 encode
函数返回密码”ldpdsdqgdehdu”。(虽不足以让我们的信息在运输过程中,确保高保密性).
当将”ldpdsdqgdehdu”放入decode
函数时,它将返回原始的”iamapandabear”,让您的朋友阅读您的原始消息.
步骤 2
移位密码是没有乐趣,直到你的妹妹算了出来后!尝试修改代码,允许我们指定一个密钥(key),并用作移位距离。这称为代换密码.
下面是一个例子:
给定密钥”aaaaaaaaaaaaaaaaaaa”,对字符串”iamapandabar”进行编码将返回原来的”iamapandable”.
给定密钥”ddddddddddddd”,编码我们的字符串”iamapandabore”会返回疑惑的”ldpdsdqgdhdu”.
在上面的示例中,我们为键值设置了 a=0
。因此,当明文添加到密钥时,我们最终得到相同的消息。所以”aaaa”不是一个理想的密钥。但是,如果我们把密钥设置为dddd
,我们将得到与 caesar 密码相同的东西.
步骤 3
任何密码中最薄弱的环节都是人。让我们通过提供随机性,并确保密钥仅包含小写字母,来使替换密码更具容错性.
如果有人根本不提交密钥,则生成一个长度至少为 100 个字符的真正随机密钥.
如果提交的密钥不只由小写字母组成,那解决方案应该以适当提醒的方式处理错误.
扩展
移位密码通过使文本略微疑惑,达到目的,但易受频率分析的影响。替换密码有助于这一点,但当密钥较短,或空格被保留时,仍然非常脆弱。稍后,你会看到一个解决这个问题的练习”crypto-square”.
如果你想在这一领域走得更远,问题引导你,走进如何以安全的方式交换密钥。看一看维基百科上的 Diffie Hellman对于该方案的最早实现之一.
资源
维基百科的替代密码http://en.wikipedia.org/wiki/Substitution_cipher
2. 开始你的表演
pub fn encode(key: &str, s: &str) -> Option<String> { unimplemented!("Use {} to encode {} using shift cipher", key, s) } pub fn decode(key: &str, s: &str) -> Option<String> { unimplemented!("Use {} to decode {} using shift cipher", key, s) } pub fn encode_random(s: &str) -> (String, String) { unimplemented!( "Generate random key with only a-z chars and encode {}. Return tuple (key, encoded s)", s ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { use std::collections::HashSet; const PLAIN_TEXT: &str = "thisismysecret"; const KEY: &str = "abcdefghij"; #[test] fn cipher_can_encode_with_given_key() { assert_eq!(encode(KEY, "aaaaaaaaaa"), Some(KEY.to_string())); } #[test] //#[ignore] fn cipher_can_decode_with_given_key() { assert_eq!(decode(KEY, "abcdefghij"), Some("aaaaaaaaaa".to_string())); } #[test] //#[ignore] fn cipher_is_reversible_given_key() { assert_eq!( decode(KEY, &encode(KEY, PLAIN_TEXT).unwrap()), Some(PLAIN_TEXT.to_string()) ); } #[test] //#[ignore] fn cipher_can_double_shift_encode() { let plain_text = "iamapandabear"; assert_eq!( encode(plain_text, plain_text), Some("qayaeaagaciai".to_string()) ); } #[test] //#[ignore] fn cipher_can_wrap_encode() { assert_eq!(encode(KEY, "zzzzzzzzzz"), Some("zabcdefghi".to_string())); } #[test] //#[ignore] fn cipher_can_encode_a_message_that_is_shorter_than_the_key() { assert_eq!(encode(KEY, "aaaaa"), Some("abcde".to_string())); } #[test] //#[ignore] fn cipher_can_decode_a_message_that_is_shorter_than_the_key() { assert_eq!(decode(KEY, "abcde"), Some("aaaaa".to_string())); } #[test] //#[ignore] fn encode_returns_none_with_an_all_caps_key() { let key = "ABCDEF"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_an_any_caps_key() { let key = "abcdEFg"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_numeric_key() { let key = "12345"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_any_numeric_key() { let key = "abcd345ef"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_empty_key() { let key = ""; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_an_all_caps_key() { let key = "ABCDEF"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_an_any_caps_key() { let key = "abcdEFg"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_numeric_key() { let key = "12345"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_any_numeric_key() { let key = "abcd345ef"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_empty_key() { let key = ""; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_random_uses_key_made_of_letters() { let (k, _) = encode_random(PLAIN_TEXT); assert!(k.chars().all(|c| c.is_ascii_lowercase())); } #[test] //#[ignore] fn encode_random_uses_key_of_100_characters_or_more() { let (k, _) = encode_random(PLAIN_TEXT); assert!(k.len() >= 100); } #[test] //#[ignore] fn encode_random_uses_randomly_generated_key() { let mut keys = HashSet::new(); let trials = 100; for _ in 0..trials { keys.insert(encode_random(PLAIN_TEXT).0); } assert_eq!(keys.len(), trials); } #[test] //#[ignore] fn encode_random_can_encode() { let (k, encoded) = encode_random("aaaaaaaaaa"); assert_eq!(encoded, k.split_at(10).0); } #[test] //#[ignore] fn encode_random_can_decode() { let (k, _) = encode_random("aaaaaaaaaa"); assert_eq!(decode(&k, k.split_at(10).0), Some("aaaaaaaaaa".to_string())); } #[test] //#[ignore] fn encode_random_is_reversible() { let (k, encoded) = encode_random(PLAIN_TEXT); assert_eq!(decode(&k, &encoded), Some(PLAIN_TEXT.to_string())); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { extern crate rand; use rand::Rng; pub fn encode_random(s: &str) -> (String, String) { let mut r = rand::thread_rng(); let mut key = String::new(); for _ in 0..100 { key.push(char::from('a' as u8 + r.gen_range(0, 26))); } let encoded = encode(&key, s); (key, encoded.unwrap()) } pub fn encode(key: &str, s: &str) -> Option<String> { shift(key, s, 1) } pub fn decode(key: &str, s: &str) -> Option<String> { shift(key, s, -1) } fn shift(key: &str, s: &str, dir: i8) -> Option<String> { if key.is_empty() { return None; } let mut o = String::new(); let mut i = 0; let mut key_arr = Vec::new(); for c in key.chars() { if !c.is_ascii_lowercase() { return None; } key_arr.push(c); } for c in s.chars() { let shift = key_arr[i % key_arr.len()] as i8 - 'a' as i8; let n = ((c as i8 - 'a' as i8 + dir * shift) % 26 + 26) % 26; o.push(char::from('a' as u8 + n as u8)); i += 1; } Some(o) } #}
填充/相关
Rail Fence Cipher
1. Readme
篱笆密码法(栅栏密码)
实现篱笆密码法的编/解码.
篱笆密码法是一种置换式密码,它从编码的方式得到它的名字。它早被古希腊人所使用。
在 栅栏 密码中,信息向下写在虚构的篱笆的连续”rail”上,然后当我们到达底部时,又向上移动(像锯齿形)。最后,消息以行读取.
例如,使用三个”Rails(栅栏)”,和加密”WE ARE DISCOVERED FLEE AT ONCE”信息,密文写道:
W . . . E . . . C . . . R . . . L . . . T . . . E
. E . R . D . S . O . E . E . F . E . A . O . C .
. . A . . . I . . . V . . . D . . . E . . . N . .
然后读出:
WECRLTEERDSOEEFEAOCAIVDEN
要解密一条消息,你要采用锯齿形读法,而密文则是一行一行看。
? . . . ? . . . ? . . . ? . . . ? . . . ? . . . ?
. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? .
. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . .
第一行有七个点,可以用”WECRRLTE”填充.
W . . . E . . . C . . . R . . . L . . . T . . . E
. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? .
. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . .
现在第二行为”ERDSOEEFEAOC”.
W . . . E . . . C . . . R . . . L . . . T . . . E
. E . R . D . S . O . E . E . F . E . A . O . C .
. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . .
最后一排”AIVDEN”.
W(1) . . . E . . . C . . . R . . . L . . . T . . . E
. E(2) . R . D . S . O . E . E . F . E . A . O . C .
. . A(3) . . . I . . . V . . . D . . . E . . . N . .
1,2,3
只是为了方便理解,不存在任何地方
如果你现在锯齿形阅读,你可以阅读原始消息.
资源
维基百科https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher
百度百科 https://baike.baidu.com/item/%E6%A0%85%E6%A0%8F%E5%AF%86%E7%A0%81
2. 开始你的表演
pub struct RailFence; impl RailFence { pub fn new(rails: u32) -> RailFence { unimplemented!("Construct a new fence with {} rails", rails) } pub fn encode(&self, text: &str) -> String { unimplemented!("Encode this text: {}", text) } pub fn decode(&self, cipher: &str) -> String { unimplemented!("Decode this ciphertext: {}", cipher) } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { /// Tests for rail-fence-cipher /// /// Generated by [script][script] using [canonical data][canonical-data] /// /// [script]: https://github.com/exercism/rust/blob/master/bin/init_exercise.py /// [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/rail-fence-cipher/canonical_data.json /// /// The tests do not expect any normalization or cleaning. /// That trade is tested in enough other exercises. /// Process a single test case for the property `encode` /// /// All cases for the `encode` property are implemented /// in terms of this function. fn process_encode_case(input: &str, rails: u32, expected: &str) { let rail_fence = RailFence::new(rails); assert_eq!(rail_fence.encode(input), expected); } /// Process a single test case for the property `decode` /// /// All cases for the `decode` property are implemented /// in terms of this function. fn process_decode_case(input: &str, rails: u32, expected: &str) { let rail_fence = RailFence::new(rails); assert_eq!(rail_fence.decode(input), expected); } // encode #[test] /// encode with two rails fn test_encode_with_two_rails() { process_encode_case("XOXOXOXOXOXOXOXOXO", 2, "XXXXXXXXXOOOOOOOOO"); } #[test] //#[ignore] /// encode with three rails fn test_encode_with_three_rails() { process_encode_case("WEAREDISCOVEREDFLEEATONCE", 3, "WECRLTEERDSOEEFEAOCAIVDEN"); } #[test] //#[ignore] /// encode with ending in the middle fn test_encode_with_ending_in_the_middle() { process_encode_case("EXERCISES", 4, "ESXIEECSR"); } // decode #[test] //#[ignore] /// decode with three rails fn test_decode_with_three_rails() { process_decode_case("TEITELHDVLSNHDTISEIIEA", 3, "THEDEVILISINTHEDETAILS"); } #[test] //#[ignore] /// decode with five rails fn test_decode_with_five_rails() { process_decode_case("EIEXMSMESAORIWSCE", 5, "EXERCISMISAWESOME"); } #[test] //#[ignore] /// decode with six rails fn test_decode_with_six_rails() { process_decode_case( "133714114238148966225439541018335470986172518171757571896261", 6, "112358132134558914423337761098715972584418167651094617711286", ); } #[test] //#[ignore] /// encode wide characters /// /// normally unicode is not part of exercism exercises, but in an exercise /// specifically oriented around shuffling characters, it seems worth ensuring /// that wide characters are handled properly /// /// this text is possibly one of the most famous haiku of all time, by /// Matsuo Bashō (松尾芭蕉) fn test_encode_wide_characters() { process_encode_case( "古池 蛙飛び込む 水の音", 3, "古飛 池蛙びむ水音 込の", ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub struct RailFence(u32); fn uncons(s: &str) -> (&str, &str) { s.split_at(s.chars().next().map_or(0, |c| c.len_utf8())) } impl RailFence { pub fn new(rails: u32) -> RailFence { RailFence(rails) } fn next(&self, down: &mut bool, rail: &mut usize) { if *down { if *rail + 1 < self.0 as usize { *rail += 1; } else { *down = false; *rail -= 1; } } else { if *rail > 0 { *rail -= 1; } else { *down = true; *rail += 1; } } } pub fn encode(&self, text: &str) -> String { let mut rails = vec![String::with_capacity(1 + (text.len() / self.0 as usize)); self.0 as usize]; let mut down = true; let mut rail = 0; for ch in text.chars() { rails[rail].push(ch); self.next(&mut down, &mut rail); } rails.join("") } pub fn decode(&self, cipher: &str) -> String { let mut rail_caps = vec![0; self.0 as usize]; let mut down = true; let mut rail = 0; for _ in cipher.chars() { rail_caps[rail] += 1; self.next(&mut down, &mut rail); } // this vector owns the text of each rail let mut rails_own = Vec::with_capacity(self.0 as usize); let mut skip = 0; for &cap in rail_caps.iter() { rails_own.push( cipher .chars() .skip(skip) .enumerate() .take_while(|&(i, _)| i < cap) .map(|(_, c)| c) .collect::<String>(), ); skip += cap; } // this vector holds string slices viewing into rails_own let mut rails: Vec<&str> = rails_own.iter().map(|r| r.as_ref()).collect(); let mut out = String::with_capacity(cipher.len()); down = true; rail = 0; while rails.iter().any(|r: &&str| r.len() > 0) { let (head, t_rail) = uncons(rails[rail]); rails[rail] = t_rail; self.next(&mut down, &mut rail); out.push_str(head); } out } } #}
填充/相关
ETL
1. Readme
ETL
ETL,是英文 Extract-Transform-Load
的缩写,用来描述将数据从来源端经过萃取、转置、加载至目的端的过程
ETL
提取转换负载(ETL)是一种很有意思的说法,”我们在这个系统中有一些遗留的遗留数据,现在我们需要在这个闪亮的新系统中使用,所以我们将迁移它.”
(通常情况下,接下来是,”我们只需要运行一次就好啦。”之后,通常会有很多怒拍额头,并抱怨自身有多么愚蠢。
目标
我们将从遗留系统中,提取一些拼字游戏分数.
旧的系统存储每一个字母的列表:
- 1 分:”A”,”E”,”I”,”O”,”U”,”L”,”N”,”R”,”S”,”T”,
- 2 分:”D”,”G”,
- 3 分:”B”、”C”、”M”、”P”,
- 4 分:”F”、”H”、”V”、”W”、”Y”,
- 5 分:”K”,
- 8 分:”J”,”X”,
- 10 分:”Q”,”Z”,
闪亮的新拼写系统存储每个字母的分数,这使得计算一个单词的分数更快、更容易。它也把字母存为小写字母,而不考虑输入字母的情况:
- “a” 值 1 分.
- “b” 值 3 分.
- “c” 值 3 分.
- “d” 值 2 分.
- 等.
你的任务,你应该选择接受它, 应将遗留数据格式,转换成闪亮的新格式。
笔记
关于得分的最后一点是,Scrabble 这款游戏在世界各地,多个国家,每种语言都有自己独特的得分表。例如,在毛利(Māori)语版本的游戏中,”E”得分为 2 分,而在夏威夷语版本中为 4 分。
资源
JunpStab 实验室团队http://jumpstartlab.com
2. 开始你的表演
use std::collections::BTreeMap; pub fn transform(h: &BTreeMap<i32, Vec<char>>) -> BTreeMap<char, i32> { unimplemented!("How will you transform the tree {:?}?", h) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_transform_one_value() { let input = input_from(&[(1, vec!['A'])]); let expected = expected_from(&[('a', 1)]); assert_eq!(expected, transform(&input)); } #[test] //#[ignore] fn test_transform_more_values() { let input = input_from(&[(1, vec!['A', 'E', 'I', 'O', 'U'])]); let expected = expected_from(&[('a', 1), ('e', 1), ('i', 1), ('o', 1), ('u', 1)]); assert_eq!(expected, transform(&input)); } #[test] //#[ignore] fn test_more_keys() { let input = input_from(&[(1, vec!['A', 'E']), (2, vec!['D', 'G'])]); let expected = expected_from(&[('a', 1), ('e', 1), ('d', 2), ('g', 2)]); assert_eq!(expected, transform(&input)); } #[test] //#[ignore] fn test_full_dataset() { let input = input_from(&[ (1, vec!['A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T']), (2, vec!['D', 'G']), (3, vec!['B', 'C', 'M', 'P']), (4, vec!['F', 'H', 'V', 'W', 'Y']), (5, vec!['K']), (8, vec!['J', 'X']), (10, vec!['Q', 'Z']), ]); let expected = expected_from(&[ ('a', 1), ('b', 3), ('c', 3), ('d', 2), ('e', 1), ('f', 4), ('g', 2), ('h', 4), ('i', 1), ('j', 8), ('k', 5), ('l', 1), ('m', 3), ('n', 1), ('o', 1), ('p', 3), ('q', 10), ('r', 1), ('s', 1), ('t', 1), ('u', 1), ('v', 4), ('w', 4), ('x', 8), ('y', 4), ('z', 10), ]); assert_eq!(expected, transform(&input)); } fn input_from(v: &[(i32, Vec<char>)]) -> BTreeMap<i32, Vec<char>> { v.iter().cloned().collect() } fn expected_from(v: &[(char, i32)]) -> BTreeMap<char, i32> { v.iter().cloned().collect() } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::BTreeMap; pub fn transform(input: &BTreeMap<i32, Vec<char>>) -> BTreeMap<char, i32> { input .iter() .flat_map(|(&n, vec)| vec.iter().map(move |c| (c.to_ascii_lowercase(), n))) .collect() } #}
填充/相关
Accumulate
1. Readme
Accumulate
实现accumulate
操作, 给出一个集合,和一个操作行为,该行为会影响到集合中的每个值,并返回一个新的,包含影响结果值的集合
如:给出数字的集合:
1,2,3,4,5
和一个平方操作:
- 平方它(
x => x * x
)
您的代码应该能够生成原集合的平方集合:
1,4,9,16,25
查看测试套件,以查看预期的函数命名.
限制
请不要使用,标准库提供的 collect/map/fmap/whatchamacallit
函数! 使用其他基本工具自己解决这个问题.
提示
看看 Fn traits 可能会有所帮助:Fn,FnMut和FnOnce.
有关将闭包传递给函数的帮助可以在“闭包作为输入参数” 章节里面,更多可看Rust by Example.
改为中文网址
如果您的函数命名不适合它们,即使它们没有运行,此练习的测试也会导致编译时错误。您可能希望对某些测试进行注释,并逐个击破。
资源
与 James Edward Gray II 的对话https://twitter.com/jeg2
2. 开始你的表演
/// What should the type of _function be? pub fn map(input: Vec<i32>, _function: ???) -> Vec<i32> { unimplemented!("Transform input vector {:?} using passed function", input); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn square(x: i32) -> i32 { x * x } #[test] fn func_single() { let input = vec![2]; let expected = vec![4]; assert_eq!(map(input, square), expected); } #[test] //#[ignore] fn func_multi() { let input = vec![2, 3, 4, 5]; let expected = vec![4, 9, 16, 25]; assert_eq!(map(input, square), expected); } #[test] //#[ignore] fn closure() { let input = vec![2, 3, 4, 5]; let expected = vec![4, 9, 16, 25]; assert_eq!(map(input, |x| x * x), expected); } #[test] //#[ignore] fn closure_floats() { let input = vec![2.0, 3.0, 4.0, 5.0]; let expected = vec![4.0, 9.0, 16.0, 25.0]; assert_eq!(map(input, |x| x * x), expected); } #[test] //#[ignore] fn strings() { let input = vec!["1".to_string(), "2".into(), "3".into()]; let expected = vec!["11".to_string(), "22".into(), "33".into()]; assert_eq!(map(input, |s| s.repeat(2)), expected); } #[test] //#[ignore] fn change_in_type() { let input: Vec<&str> = vec!["1", "2", "3"]; let expected: Vec<String> = vec!["1".into(), "2".into(), "3".into()]; assert_eq!(map(input, |s| s.to_string()), expected); } #[test] //#[ignore] fn mutating_closure() { let mut counter = 0; let input = vec![-2, 3, 4, -5]; let expected = vec![2, 3, 4, 5]; let result = map(input, |x: i64| { counter += 1; x.abs() }); assert_eq!(result, expected); assert_eq!(counter, 4); } #[test] //#[ignore] fn minimal_bounds_on_input_and_output() { // must be able to accept arbitrary input and output types struct Foo; struct Bar; map(vec![Foo], |_| Bar); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn map<F, T, U>(values: Vec<T>, mut f: F) -> Vec<U> where F: FnMut(T) -> U, { let mut v = Vec::with_capacity(values.len()); for val in values { v.push(f(val)); } v } #}
填充/相关
Acronym
1. Readme
缩写
将短语转换为,其首字母缩写词。
技术人员都喜欢他们的 TLA(三字母缩略语(Three Letter Acronyms)显得高大上)!
通过编写将诸如 Portable Network Graphics 之类的长名称,转换为其首字母缩略词(PNG)的程序,帮助生成一些术语。
资源
Julien Vanierhttps://github.com/monkbroc
2. 开始你的表演
pub fn abbreviate(phrase: &str) -> String { unimplemented!("Given the phrase '{}', return its acronym", phrase); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn empty() { assert_eq!(abbreviate(""), ""); } #[test] //#[ignore] fn basic() { assert_eq!(abbreviate("Portable Network Graphics"), "PNG"); } #[test] //#[ignore] fn lowercase_words() { assert_eq!(abbreviate("Ruby on Rails"), "ROR"); } #[test] //#[ignore] fn camelcase() { assert_eq!(abbreviate("HyperText Markup Language"), "HTML"); } #[test] //#[ignore] fn punctuation() { assert_eq!(abbreviate("First In, First Out"), "FIFO"); } #[test] //#[ignore] fn all_caps_words() { assert_eq!(abbreviate("PHP: Hypertext Preprocessor"), "PHP"); } #[test] //#[ignore] fn non_acronym_all_caps_word() { assert_eq!(abbreviate("GNU Image Manipulation Program"), "GIMP"); } #[test] //#[ignore] fn hyphenated() { assert_eq!( abbreviate("Complementary metal-oxide semiconductor"), "CMOS" ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn abbreviate(phrase: &str) -> String { phrase .split(|c: char| c.is_whitespace() || !c.is_alphanumeric()) .flat_map(|word| split_camel(word)) .filter_map(|word| word.chars().next()) .collect::<String>() .to_uppercase() } fn split_camel(phrase: &str) -> Vec<String> { let chars: Vec<char> = phrase.chars().collect(); let mut words: Vec<String> = Vec::new(); let mut word_start: usize = 0; for (i, c) in chars.iter().enumerate() { if i == chars.len() - 1 || c.is_lowercase() && chars[i + 1].is_uppercase() { words.push(chars[word_start..i + 1].iter().cloned().collect()); word_start = i + 1; } } words } #}
填充/相关
Sieve
1. Readme
素数筛
使用 Eratosthenes 的 Sieve 查找从 2 到给定数字的所有素数.
这是一种简单且历史悠久的筛法,用来找出一定范围内所有的素数。
所使用的原理是从 2 开始,将每个素数的各个倍数,标记成合数。一个素数的各个倍数,是一个差为此素数本身的等差数列。此为这个筛法和试除法不同的关键之处,后者是以素数来测试每个待测数能否被整除。
埃拉托斯特尼筛法是列出所有小素数最有效的方法之一,其名字来自于古希腊数学家埃拉托斯特尼,并且被描述在另一位古希腊数学家尼科马库斯所著的《算术入门》中。
维基百科文章有一个有用的图解解释算法:
请注意,这是一个非常具体的算法,并且测试不会检查您是否实现了算法,只要您已经提出了正确的素数列表.https://zh.wikipedia.org/wiki/埃拉托斯特尼筛法
一个好的第一个测试是,检查你不使用除法或余数运算(div, /, mod or % 具体语言所具有的)
资源
维基百科的 素数 筛https://zh.wikipedia.org/wiki/埃拉托斯特尼筛法
2. 开始你的表演
pub fn primes_up_to(upper_bound: u64) -> Vec<u64> { unimplemented!("Construct a vector of all primes up to {}", upper_bound); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn limit_lower_than_the_first_prime() { assert_eq!(primes_up_to(1), []); } #[test] //#[ignore] fn limit_is_the_first_prime() { assert_eq!(primes_up_to(2), [2]); } #[test] //#[ignore] fn primes_up_to_10() { assert_eq!(primes_up_to(10), [2, 3, 5, 7]); } #[test] //#[ignore] fn limit_is_prime() { assert_eq!(primes_up_to(13), [2, 3, 5, 7, 11, 13]); } #[test] //#[ignore] fn limit_of_1000() { let expected = vec![ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, ]; assert_eq!(primes_up_to(1000), expected); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn primes_up_to(limit: i32) -> Vec<i32> { let mut integers = (1..limit).map(|x| x + 1).collect::<Vec<i32>>(); let mut p = Some(2); while let Some(y) = p { integers.retain(|&x| (x == y) || (x % y != 0)); p = integers.clone().into_iter().find(|x| *x > y); } integers } #}
填充/相关
RNA Transcription
1. Readme
RNA 转录
给定 DNA 链,返回其 RNA 补体(每个 RNA 转录).
DNA 和 RNA 链都是核苷酸序列.
DNA 中发现的四个核苷酸是腺嘌呤(A),胞嘧啶(C),鸟嘌呤(G)和胸腺嘧啶(T).
RNA 中发现的四个核苷酸是腺嘌呤(A),胞嘧啶(C),鸟嘌呤(G)和尿嘧啶(T).
给定 DNA 链,其转录的 RNA 链,通过用其互补物替换每个核苷酸而形成:
G
- >C
C
- >G
T
- >A
A
- >U
关于 Rust 实现的注释
通过在公共结构中使用私有字段,new
函数会返回Option
要么Result
(在这里DNA::new
&RNA::new
),我们可以保证DNA
内部的表达是正确的。因为每个有效的 DNA 字符串,都有一个有效的 RNA 字符串,所以我们不需要从to_rna
返回一个Result
/Option
.
这解释了您将在测试中看到的类型签名。
资源
Hyperphysicshttp://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html
2. 开始你的表演
#[derive(Debug, PartialEq)] pub struct DNA; #[derive(Debug, PartialEq)] pub struct RNA; impl DNA { pub fn new(dna: &str) -> Result<DNA, usize> { unimplemented!("Construct new DNA from '{}' string. If string contains invalid nucleotides return index of first invalid nucleotide", dna); } pub fn to_rna(self) -> RNA { unimplemented!("Transform DNA {:?} into corresponding RNA", self); } } impl RNA { pub fn new(rna: &str) -> Result<RNA, usize> { unimplemented!("Construct new RNA from '{}' string. If string contains invalid nucleotides return index of first invalid nucleotide", rna); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_valid_dna_input() { assert!(DNA::new("GCTA").is_ok()); } #[test] //#[ignore] fn test_valid_rna_input() { assert!(RNA::new("CGAU").is_ok()); } #[test] //#[ignore] fn test_invalid_dna_input() { // Invalid character assert_eq!(DNA::new("X").err(), Some(0)); // Valid nucleotide, but invalid in context assert_eq!(DNA::new("U").err(), Some(0)); // Longer string with contained errors assert_eq!(DNA::new("ACGTUXXCTTAA").err(), Some(4)); } #[test] //#[ignore] fn test_invalid_rna_input() { // Invalid character assert!(RNA::new("X").is_err()); // Valid nucleotide, but invalid in context assert!(RNA::new("T").is_err()); // Longer string with contained errors assert!(RNA::new("ACGUTTXCUUAA").is_err()); } #[test] //#[ignore] fn test_acid_equals_acid() { assert_eq!(DNA::new("CGA").unwrap(), DNA::new("CGA").unwrap()); assert_ne!(DNA::new("CGA").unwrap(), DNA::new("AGC").unwrap()); assert_eq!(RNA::new("CGA").unwrap(), RNA::new("CGA").unwrap()); assert_ne!(RNA::new("CGA").unwrap(), RNA::new("AGC").unwrap()); } #[test] //#[ignore] fn test_transcribes_cytosine_guanine() { assert_eq!(RNA::new("G").unwrap(), DNA::new("C").unwrap().to_rna()); } #[test] //#[ignore] fn test_transcribes_guanine_cytosine() { assert_eq!(RNA::new("C").unwrap(), DNA::new("G").unwrap().to_rna()); } #[test] //#[ignore] fn test_transcribes_adenine_uracil() { assert_eq!(RNA::new("U").unwrap(), DNA::new("A").unwrap().to_rna()); } #[test] //#[ignore] fn test_transcribes_thymine_to_adenine() { assert_eq!(RNA::new("A").unwrap(), DNA::new("T").unwrap().to_rna()); } #[test] //#[ignore] fn test_transcribes_all_dna_to_rna() { assert_eq!( RNA::new("UGCACCAGAAUU").unwrap(), DNA::new("ACGTGGTCTTAA").unwrap().to_rna() ) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { /// The possible nucleotides in DNA and RNA #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Nucleotide { Adenine, Cytosine, Guanine, Thymine, Uracil, } impl Nucleotide { /// Parses a nucleotide from its character value, if valid. fn from_char(ch: char) -> Option<Nucleotide> { Some(match ch { 'A' => Nucleotide::Adenine, 'C' => Nucleotide::Cytosine, 'G' => Nucleotide::Guanine, 'T' => Nucleotide::Thymine, 'U' => Nucleotide::Uracil, _ => { return None; } }) } } /// A vector of nucleotides /// /// Guaranteed that Uracil is not present thanks to `new`. #[derive(Debug, Eq, PartialEq, Clone)] pub struct DNA(Vec<Nucleotide>); impl DNA { /// Parse a DNA string, checking it is valid. /// /// The error value is the first invalid character index (char index, not utf8). pub fn new(input: &str) -> Result<DNA, usize> { let mut out = Vec::new(); for (idx, ch) in input.chars().enumerate() { match Nucleotide::from_char(ch) { Some(Nucleotide::Uracil) | None => { return Err(idx); } Some(n) => { out.push(n); } } } Ok(DNA(out)) } pub fn to_rna(mut self) -> RNA { for nuc in self.0.iter_mut() { *nuc = match *nuc { Nucleotide::Adenine => Nucleotide::Uracil, Nucleotide::Cytosine => Nucleotide::Guanine, Nucleotide::Guanine => Nucleotide::Cytosine, Nucleotide::Thymine => Nucleotide::Adenine, Nucleotide::Uracil => unreachable!(), } } RNA(self.0) } } #[derive(Debug, Eq, PartialEq, Clone)] pub struct RNA(Vec<Nucleotide>); impl RNA { /// Parse a RNA string, checking it is valid. /// /// The error value is the first invalid character index (char index, not utf8). pub fn new(input: &str) -> Result<RNA, usize> { let mut out = Vec::new(); for (idx, ch) in input.chars().enumerate() { match Nucleotide::from_char(ch) { Some(Nucleotide::Thymine) | None => { return Err(idx); } Some(n) => { out.push(n); } } } Ok(RNA(out)) } } #}
填充/相关
Triangle
1. Readme
三角形
确定三角形是等边、等腰还是不等边三角形.
一个等边的三角形,三条边都有相同的长度。
一个等腰的三角形,至少两边相同的长度。(有时它被指定为两边长度完全相同,但是为了这个练习的目的,我们的说法是,至少两边。)
一不等边的三角形的两边各有不同的长度。
笔记
为了能形成三角形,所有的边都必须的长度>0,并且任何两边的长度之和必须大于或等于第三边的长度。见不等三角形.
挖深点
两边长度之和,是等于第三边,就叫做退化三角形 - 因它有零面积,看起来像一条直线。随意添加你自己的代码/测试来检查退化三角形.
Rust 中 三角形帮助
实现这一点可以采取多种形式。以下是一些可以帮助你的话题,这取决于你采取的方法.
或者也许你会想出一种不用那些方法的方法!
非整数长度
基础练习测试三角形的边,都是整数的识别。然而,一些三角形不能用纯整数表示。一个简单的例子是一个直角三角形(也是等边三角形,两等边分开 90 度),其等边都有 1 的长度。它的斜边是 2 的平方根,这是一个无理数: 没有简单的乘法,可以将这个数表示为整数。
重写分析函数来处理整数和浮点情况,老乏味啦,特别是对于所有潜在的整数和浮点类型的情况来说:如给定的 8, 16, 32、64 和 128 位宽度的已签名和未签名的变体,甚至再考虑浮点数,就多出了 10 个根本上相同的代码,!
这有个更好的方法:generics。 把你的三角形重写为Triangle<T>
,这样您就可以编写一次代码,并将生成所有这些专门化的工作交给编译器。注意,为了使用数学运算,您需要将泛型类型限制为,支持使用特征的那些运算的类型。
有些加分测试,这些测试,能测试您在浮点数字上的实现。若要启用它们,请使用generic
特征标记,像这样:
cargo test --features generic
资源
Ruby-科恩三角形项目,第 1 和 2 部分http://rubykoans.com
2. 开始你的表演
pub struct Triangle; impl Triangle { pub fn build(sides: [u64; 3]) -> Option<Triangle> { unimplemented!("Construct new Triangle from following sides: {:?}. Return None if the sides are invalid.", sides); } pub fn is_equilateral(&self) -> bool { unimplemented!("Determine if the Triangle is equilateral."); } pub fn is_scalene(&self) -> bool { unimplemented!("Determine if the Triangle is scalene."); } pub fn is_isosceles(&self) -> bool { unimplemented!("Determine if the Triangle is isosceles."); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn positive_length_sides_are_ok() { let sides = [2, 2, 2]; let triangle = Triangle::build(sides); assert!(triangle.is_some()); } #[test] //#[ignore] fn zero_length_sides_are_illegal() { let sides = [0, 0, 0]; let triangle = Triangle::build(sides); assert!(triangle.is_none()); } #[test] //#[ignore] fn equilateral_triangles_have_equal_sides() { let sides = [2, 2, 2]; let triangle = Triangle::build(sides).unwrap(); assert!(triangle.is_equilateral()); assert!(!triangle.is_scalene()); } #[test] //#[ignore] fn larger_equilateral_triangles_have_equal_sides() { let sides = [10, 10, 10]; let triangle = Triangle::build(sides).unwrap(); assert!(triangle.is_equilateral()); assert!(!triangle.is_scalene()); } #[test] //#[ignore] fn isosceles_triangles_have_two_equal_sides_one() { let sides = [3, 4, 4]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(triangle.is_isosceles()); assert!(!triangle.is_scalene()); } #[test] //#[ignore] fn isosceles_triangles_have_two_equal_sides_two() { let sides = [4, 4, 3]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(triangle.is_isosceles()); assert!(!triangle.is_scalene()); } #[test] //#[ignore] fn isosceles_triangles_have_two_equal_sides_three() { let sides = [4, 3, 4]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(triangle.is_isosceles()); assert!(!triangle.is_scalene()); } #[test] //#[ignore] fn isosceles_triangles_have_two_equal_sides_four() { let sides = [4, 7, 4]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(triangle.is_isosceles()); assert!(!triangle.is_scalene()); } #[test] //#[ignore] fn scalene_triangle_has_no_equal_sides_one() { let sides = [3, 4, 5]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(!triangle.is_isosceles()); assert!(triangle.is_scalene()); } #[test] //#[ignore] fn scalene_triangle_has_no_equal_sides_two() { let sides = [5, 4, 6]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(!triangle.is_isosceles()); assert!(triangle.is_scalene()); } #[test] //#[ignore] fn scalene_triangle_has_no_equal_sides_three() { let sides = [10, 11, 12]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(!triangle.is_isosceles()); assert!(triangle.is_scalene()); } #[test] //#[ignore] fn scalene_triangle_has_no_equal_sides_four() { let sides = [5, 4, 2]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(!triangle.is_isosceles()); assert!(triangle.is_scalene()); } #[test] //#[ignore] fn sum_of_two_sides_must_equal_or_exceed_the_remaining_side_one() { let sides = [7, 3, 2]; let triangle = Triangle::build(sides); assert!(triangle.is_none()); } #[test] //#[ignore] fn sum_of_two_sides_must_equal_or_exceed_the_remaining_side_two() { let sides = [1, 1, 3]; let triangle = Triangle::build(sides); assert!(triangle.is_none()); } #[test] //#[ignore] #[cfg(feature = "generic")] fn scalene_triangle_with_floating_point_sides() { let sides = [0.4, 0.6, 0.3]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(!triangle.is_isosceles()); assert!(triangle.is_scalene()); } #[test] //#[ignore] #[cfg(feature = "generic")] fn equilateral_triangles_with_floating_point_sides() { let sides = [0.2, 0.2, 0.2]; let triangle = Triangle::build(sides).unwrap(); assert!(triangle.is_equilateral()); assert!(!triangle.is_scalene()); } #[test] //#[ignore] #[cfg(feature = "generic")] fn isosceles_triangle_with_floating_point_sides() { let sides = [0.3, 0.4, 0.4]; let triangle = Triangle::build(sides).unwrap(); assert!(!triangle.is_equilateral()); assert!(triangle.is_isosceles()); assert!(!triangle.is_scalene()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::ops::Add; pub struct Triangle<T> { sides: [T; 3], } impl<T> Triangle<T> where T: Copy + PartialEq + PartialOrd + Add<Output = T> + Default, { fn valid_sides(&self) -> bool { (self.sides.iter().all(|&s| s > T::default())) && (self.sides[0] + self.sides[1] >= self.sides[2]) && (self.sides[1] + self.sides[2] >= self.sides[0]) && (self.sides[2] + self.sides[0] >= self.sides[1]) } fn count_distinct_pairs(&self) -> usize { [(0, 1), (0, 2), (1, 2)] .iter() .map(|&(a, b)| if self.sides[a] != self.sides[b] { 1 } else { 0 }) .sum() } pub fn build(sides: [T; 3]) -> Option<Triangle<T>> { let t = Triangle { sides: sides }; if t.valid_sides() { Some(t) } else { None } } pub fn is_equilateral(&self) -> bool { self.count_distinct_pairs() == 0 } pub fn is_isosceles(&self) -> bool { self.count_distinct_pairs() == 2 } pub fn is_scalene(&self) -> bool { self.count_distinct_pairs() == 3 } } #}
填充/相关
Roman Numerals
1. Readme
罗马数字
写一个函数,从普通数字,转换成罗马数字.
罗马人是一群聪明的人。他们征服了欧洲大部分国家,统治了几百年。他们发明了混凝土和直路,甚至 Bikinis 夜店。他们从来没有发现过的一件事就是数字零。这使得写作和约会他们的功绩的广泛历史稍有挑战性,但他们提出的数字系统仍在使用。例如,英国广播公司使用罗马数字制定他们的节目。
罗马人用字母 I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和 M(1000) 写数字(注意这些字母有很多直线,因此很容易侵入石碑)。
1 => I
10 => X
7 => VII
不需要能 转换 超过 3000 的罗马数字
在较大的罗马数字的右边记上较小的罗马数字,表示大数字加小数字。 在较大的罗马数字的左边记上较小的罗马数字,表示大数字减小数字。
要在实践中看到这一点,请考虑 1990 的例子.
在罗马数字中,1990 是 MCMXC:
1000=M 900=CM 90=XC
CM = 1000 - 100 = 900
2008 被写成 MMVIII:
2000=MM 8=Ⅷ
参见:http://www.novaroma.org/via_romana/numbers.html
资源
维基百科https://zh.wikipedia.org/wiki/%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97
2. 开始你的表演
use std::fmt::{Display, Formatter, Result}; pub struct Roman; impl Display for Roman { fn fmt(&self, _f: &mut Formatter) -> Result { unimplemented!("Return a roman-numeral string representation of the Roman object"); } } impl From<u32> for Roman { fn from(num: u32) -> Self { unimplemented!("Construct a Roman object from the '{}' number", num); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_one() { assert_eq!("I", Roman::from(1).to_string()); } #[test] //#[ignore] fn test_two() { assert_eq!("II", Roman::from(2).to_string()); } #[test] //#[ignore] fn test_three() { assert_eq!("III", Roman::from(3).to_string()); } #[test] //#[ignore] fn test_four() { assert_eq!("IV", Roman::from(4).to_string()); } #[test] //#[ignore] fn test_five() { assert_eq!("V", Roman::from(5).to_string()); } #[test] //#[ignore] fn test_six() { assert_eq!("VI", Roman::from(6).to_string()); } #[test] //#[ignore] fn test_nine() { assert_eq!("IX", Roman::from(9).to_string()); } #[test] //#[ignore] fn test_twenty_seven() { assert_eq!("XXVII", Roman::from(27).to_string()); } #[test] //#[ignore] fn test_forty_eight() { assert_eq!("XLVIII", Roman::from(48).to_string()); } #[test] //#[ignore] fn test_fifty_nine() { assert_eq!("LIX", Roman::from(59).to_string()); } #[test] //#[ignore] fn test_ninety_three() { assert_eq!("XCIII", Roman::from(93).to_string()); } #[test] //#[ignore] fn test_141() { assert_eq!("CXLI", Roman::from(141).to_string()); } #[test] //#[ignore] fn test_163() { assert_eq!("CLXIII", Roman::from(163).to_string()); } #[test] //#[ignore] fn test_402() { assert_eq!("CDII", Roman::from(402).to_string()); } #[test] //#[ignore] fn test_575() { assert_eq!("DLXXV", Roman::from(575).to_string()); } #[test] //#[ignore] fn test_911() { assert_eq!("CMXI", Roman::from(911).to_string()); } #[test] //#[ignore] fn test_1024() { assert_eq!("MXXIV", Roman::from(1024).to_string()); } #[test] //#[ignore] fn test_3000() { assert_eq!("MMM", Roman::from(3000).to_string()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::fmt; static ROMAN_MAP: [(usize, &'static str); 13] = [ (1, "I"), (4, "IV"), (5, "V"), (9, "IX"), (10, "X"), (40, "XL"), (50, "L"), (90, "XC"), (100, "C"), (400, "CD"), (500, "D"), (900, "CM"), (1000, "M"), ]; pub struct Roman { num: usize, } impl From<usize> for Roman { fn from(i: usize) -> Self { Roman::new(i) } } impl fmt::Display for Roman { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut start = self.num.clone(); let mut result = String::new(); for &(numeric, roman_string) in ROMAN_MAP.into_iter().rev() { while start >= numeric { result.push_str(roman_string); start = start - numeric; } } write!(f, "{}", result) } } impl Roman { fn new(num: usize) -> Roman { Roman { num: num } } } #}
填充/相关
All Your Base
1. Readme
你的所有基本
将一个数字,表示为一个基数中的数字序列,并转为其他基本
实施通用基本转换。给出一个a参数,表示为数字序列,将其转换为基数b。
笔记
- 尝试自己实现转换。请勿使用其他内容为您执行转换.
关于位置表示法
在位置表示法中,以数字表示b可以被理解为权力的线性组合b.
数字 42,基本为 10,意思是:
(4 * 10^1) + (2 * 10^0)
数字 101010,基本为 2,意思是:
(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)
号码 1120,基本为 3,意思是:
(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)
我想你明白了!
是。上面这三个数字完全一样。恭喜!
2. 开始你的表演
#[derive(Debug, PartialEq)] pub enum Error { InvalidInputBase, InvalidOutputBase, InvalidDigit(u32), } /// /// Convert a number between two bases. /// /// A number is any slice of digits. /// A digit is any unsigned integer (e.g. u8, u16, u32, u64, or usize). /// Bases are specified as unsigned integers. /// /// Return an `Err(.)` if the conversion is impossible. /// The tests do not test for specific values inside the `Err(.)`. /// /// /// You are allowed to change the function signature as long as all test still pass. /// /// /// Example: /// Input /// number: &[4, 2] /// from_base: 10 /// to_base: 2 /// Result /// Ok(vec![1, 0, 1, 0, 1, 0]) /// /// The example corresponds to converting the number 42 from decimal /// which is equivalent to 101010 in binary. /// /// /// Notes: /// * The empty slice ( "[]" ) is equal to the number 0. /// * Never output leading 0 digits. However, your function must be able to /// process input with leading 0 digits. /// pub fn convert(number: &[u32], from_base: u32, to_base: u32) -> Result<Vec<u32>, Error> { unimplemented!( "Convert {:?} from base {} to base {}", number, from_base, to_base ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn single_bit_one_to_decimal() { let input_base = 2; let input_digits = &[1]; let output_base = 10; let output_digits = vec![1]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn binary_to_single_decimal() { let input_base = 2; let input_digits = &[1, 0, 1]; let output_base = 10; let output_digits = vec![5]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn single_decimal_to_binary() { let input_base = 10; let input_digits = &[5]; let output_base = 2; let output_digits = vec![1, 0, 1]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn binary_to_multiple_decimal() { let input_base = 2; let input_digits = &[1, 0, 1, 0, 1, 0]; let output_base = 10; let output_digits = vec![4, 2]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn decimal_to_binary() { let input_base = 10; let input_digits = &[4, 2]; let output_base = 2; let output_digits = vec![1, 0, 1, 0, 1, 0]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn trinary_to_hexadecimal() { let input_base = 3; let input_digits = &[1, 1, 2, 0]; let output_base = 16; let output_digits = vec![2, 10]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn hexadecimal_to_trinary() { let input_base = 16; let input_digits = &[2, 10]; let output_base = 3; let output_digits = vec![1, 1, 2, 0]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn fifteen_bit_integer() { let input_base = 97; let input_digits = &[3, 46, 60]; let output_base = 73; let output_digits = vec![6, 10, 45]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn empty_list() { let input_base = 2; let input_digits = &[]; let output_base = 10; let output_digits = vec![]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn single_zero() { let input_base = 10; let input_digits = &[0]; let output_base = 2; let output_digits = vec![]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn multiple_zeros() { let input_base = 10; let input_digits = &[0, 0, 0]; let output_base = 2; let output_digits = vec![]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn leading_zeros() { let input_base = 7; let input_digits = &[0, 6, 0]; let output_base = 10; let output_digits = vec![4, 2]; assert_eq!( convert(input_digits, input_base, output_base), Ok(output_digits) ); } #[test] //#[ignore] fn invalid_positive_digit() { let input_base = 2; let input_digits = &[1, 2, 1, 0, 1, 0]; let output_base = 10; assert_eq!( convert(input_digits, input_base, output_base), Err(Error::InvalidDigit(2)) ); } #[test] //#[ignore] fn input_base_is_one() { let input_base = 1; let input_digits = &[]; let output_base = 10; assert_eq!( convert(input_digits, input_base, output_base), Err(Error::InvalidInputBase) ); } #[test] //#[ignore] fn output_base_is_one() { let input_base = 2; let input_digits = &[1, 0, 1, 0, 1, 0]; let output_base = 1; assert_eq!( convert(input_digits, input_base, output_base), Err(Error::InvalidOutputBase) ); } #[test] //#[ignore] fn input_base_is_zero() { let input_base = 0; let input_digits = &[]; let output_base = 10; assert_eq!( convert(input_digits, input_base, output_base), Err(Error::InvalidInputBase) ); } #[test] //#[ignore] fn output_base_is_zero() { let input_base = 10; let input_digits = &[7]; let output_base = 0; assert_eq!( convert(input_digits, input_base, output_base), Err(Error::InvalidOutputBase) ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub type Digit = u32; #[derive(Debug, PartialEq)] pub enum Error { InvalidInputBase, InvalidOutputBase, InvalidDigit(Digit), } /// /// Convert a number between two bases. /// /// A number is any slice of digits. /// A digit is any unsigned integer (e.g. u8, u16, u32, u64, or usize). /// Bases are specified as unsigned integers. /// /// Return an `Err(.)` if the conversion is impossible. /// The tests do not test for specific values inside the `Err(.)`. /// /// /// You are allowed to change the function signature as long as all test still pass. /// /// /// Example: /// Input /// number: &[4, 2] /// from_base: 10 /// to_base: 2 /// Result /// Ok(vec![1, 0, 1, 0, 1, 0]) /// /// The example corresponds to converting the number 42 from decimal /// which is equivalent to 101010 in binary. /// /// /// Notes: /// * The empty slice ( "[]" ) is equal to the number 0. /// * Never output leading 0 digits. However, your function must be able to /// process input with leading 0 digits. /// pub fn convert<P: AsRef<[Digit]>>( digits: P, from_base: Digit, to_base: Digit, ) -> Result<Vec<Digit>, Error> { // check that both bases are in the correct range if from_base < 2 { return Err(Error::InvalidInputBase); } if to_base < 2 { return Err(Error::InvalidOutputBase); } // check that all digits are in the correct range specified by the base if let Some(&invalid) = digits.as_ref().iter().find(|&num| *num >= from_base) { return Err(Error::InvalidDigit(invalid)); } // convert all digits into a single large number let mut immediate = digits .as_ref() .iter() .rev() .enumerate() .map(|(i, &num)| num * from_base.pow(i as u32)) .fold(0, |accu, num| accu + num); // convert number into digits let mut res = Vec::new(); while immediate > 0 { res.push(immediate % to_base); immediate /= to_base; } // fix order of digits res.reverse(); Ok(res) } #}
填充/相关
Grade School
1. Readme
学级
根据学生的姓名以及他们所处的年级,为学校创建一个名册.
最后,你应该能够:
- 将学生的姓名添加到年级名册
- “Add Jim to grade 2.”
- “OK.”
- 获取所有注册年级的学生列表
- “哪个学生在二年级?”
- “We’ve only got Jim just now.”
- 获取所有年级所有学生的排序列表。年级应分为 1,2,3 级,年级中的学生应按名称按字母顺序排序。
- “谁现在都在学校就读?”
- “Grade 1: Anna, Barb, and Charlie. Grade 2: Alex, Peter, and Zoe. Grade 3…”
- “谁现在都在学校就读?”
请注意,我们所有学生只有一个名字.(这是一个小镇,你想要什么?)
加分
你是否通过了测试,且代码干净了?如果您愿意,可以尝试以下一些额外的事情:
- 如果您使用的语言具有可变数据结构,并且您的实现,会不会让外部代码,能直接改变学校的内部数据库,请查看是否可以阻止这种情况。随意尝试其他测试.
资源
与 gSchool 的 Phil Battos 进行配对http://gschool.it
2. 开始你的表演
pub struct School {} impl School { pub fn new() -> School { unimplemented!() } pub fn add(&mut self, grade: u32, student: &str) { unimplemented!("Add {} to the roster for {}", student, grade) } pub fn grades(&self) -> Vec<u32> { unimplemented!() } // If grade returned an `Option<&Vec<String>>`, // the internal implementation would be forced to keep a `Vec<String>` to lend out. // By returning an owned vector instead, // the internal implementation is free to use whatever it chooses. pub fn grade(&self, grade: u32) -> Option<Vec<String>> { unimplemented!("Return the list of students in {}", grade) } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn some_strings(v: &[&str]) -> Option<Vec<String>> { Some(v.iter().map(|s| s.to_string()).collect()) } #[test] fn test_grades_for_empty_school() { let s = School::new(); assert_eq!(s.grades(), vec![]); } #[test] //#[ignore] fn test_grades_for_one_student() { let mut s = School::new(); s.add(2, "Aimee"); assert_eq!(s.grades(), vec![2]); } #[test] //#[ignore] fn test_grades_for_several_students_are_sorted() { let mut s = School::new(); s.add(2, "Aimee"); s.add(7, "Logan"); s.add(4, "Blair"); assert_eq!(s.grades(), vec![2, 4, 7]); } #[test] //#[ignore] fn test_grades_when_several_students_have_the_same_grade() { let mut s = School::new(); s.add(2, "Aimee"); s.add(2, "Logan"); s.add(2, "Blair"); assert_eq!(s.grades(), vec![2]); } #[test] //#[ignore] fn test_grade_for_empty_school() { let s = School::new(); assert_eq!(s.grade(1), None); } #[test] //#[ignore] fn test_grade_when_no_students_have_that_grade() { let mut s = School::new(); s.add(7, "Logan"); assert_eq!(s.grade(1), None); } #[test] //#[ignore] fn test_grade_for_one_student() { let mut s = School::new(); s.add(2, "Aimee"); assert_eq!(s.grade(2), some_strings(&["Aimee"])); } #[test] //#[ignore] fn test_grade_returns_students_sorted_by_name() { let mut s = School::new(); s.add(2, "James"); s.add(2, "Blair"); s.add(2, "Paul"); assert_eq!(s.grade(2), some_strings(&["Blair", "James", "Paul"])); } #[test] //#[ignore] fn test_add_students_to_different_grades() { let mut s = School::new(); s.add(3, "Chelsea"); s.add(7, "Logan"); assert_eq!(s.grades(), vec![3, 7]); assert_eq!(s.grade(3), some_strings(&["Chelsea"])); assert_eq!(s.grade(7), some_strings(&["Logan"])); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; pub struct School { grades: HashMap<u32, Vec<String>>, } impl School { pub fn new() -> School { School { grades: HashMap::new(), } } pub fn add(&mut self, grade: u32, student: &str) { let entry = self.grades.entry(grade).or_insert(Vec::new()); entry.push(student.to_string()); entry.sort(); } pub fn grades(&self) -> Vec<u32> { let mut s = self.grades.keys().cloned().collect::<Vec<u32>>(); s.sort(); s } pub fn grade(&self, grade: u32) -> Option<Vec<String>> { self.grades.get(&grade).map(|v| v.iter().cloned().collect()) } } #}
填充/相关
Binary Search
1. Readme
二分查找
实现二分查找算法.
搜索已排序的集合是一项常见任务。字典是定义单词的排序列表。有了一个词,就可以找到它的定义。电话簿是人员姓名,地址和电话号码的分类列表。知道某人的姓名可以让他们快速找到他们的电话号码和地址。
如果要搜索的列表包含多个项目(比如十几个),则二分查找将比线性搜索需要更少的比较,但它强制要求对列表进行排序。
在计算机科学中,二分查找或半间隔搜索算法在按键值排序的数组中,查找指定输入值(搜索”关键字”)的位置。
在每个步骤中,算法将搜索关键字值与数组中间元素的值进行比较。
如果匹配,则找到匹配元素,并返回其索引或位置。
否则,如果搜索关键字小于中间元素的键,则算法在中间元素左侧的子阵列上重复其操作,或者如果搜索关键字更大,则在右侧的子阵列上重复其操作。
如果要搜索的剩余阵列为空,则在阵列中找不到该键值,并返回特殊的”not found”指示。
二分查找将每次迭代检查的项目数减半,因此定位项目(或确定其不存在)需要对数时间。二分搜索是一种对半分,并渐进的搜索算法。
限制
Rust 已在其标准库中提供了一个二分查找 函数.对于本练习,您不应使用此函数,而应使用其他基本工具.
提示
Slices除了具有通过索引,正常访问元素(slice[索引]),还有很多有用的函数,像split_at给予 子区间(slice[start..end]).
您可以通过索引,使用无聊的旧元素访问来解决此练习,但也许其他提供的函数可以使您的代码更清晰,更安全.
加分
你是否通过了测试并且代码干净了?如果你愿意,你可以尝试一些其他的东西.
- 目前,您的查找函数可能仅适用于数字切片,但 Rust 类型系统足够灵活,可以创建一个查找函数,该函数适用于包含可排序元素的所有切片.
- 此外,此查找函数不仅可以在切片上工作,还可以在 Vec 或数组上同时工作.
要运行加分测试,请删除#[ignore]
标记,并执行测试generic
函数,像这样:
$ cargo test --features generic
加分的提示
- 为了使您的函数与所有可排序的元素一起使用,请查看Ord Trait.
- 要使您的函数直接在 Vec 和 Array 上运行,您可以使用AsRef Trait
资源
维基百科http://en.wikipedia.org/wiki/Binary_search_algorithm
2. 开始你的表演
pub fn find(array: &[i32], key: i32) -> Option<usize> { unimplemented!( "Using the binary search algorithm, find the element '{}' in the array '{:?}' and return its index.", key, array ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn finds_a_value_in_an_array_with_one_element() { assert_eq!(find(&[6], 6), Some(0)); } #[test] //#[ignore] fn finds_first_value_in_an_array_with_two_element() { assert_eq!(find(&[1, 2], 1), Some(0)); } #[test] //#[ignore] fn finds_second_value_in_an_array_with_two_element() { assert_eq!(find(&[1, 2], 2), Some(1)); } #[test] //#[ignore] fn finds_a_value_in_the_middle_of_an_array() { assert_eq!(find(&[1, 3, 4, 6, 8, 9, 11], 6), Some(3)); } #[test] //#[ignore] fn finds_a_value_at_the_beginning_of_an_array() { assert_eq!(find(&[1, 3, 4, 6, 8, 9, 11], 1), Some(0)); } #[test] //#[ignore] fn finds_a_value_at_the_end_of_an_array() { assert_eq!(find(&[1, 3, 4, 6, 8, 9, 11], 11), Some(6)); } #[test] //#[ignore] fn finds_a_value_in_an_array_of_odd_length() { assert_eq!( find(&[1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634], 144), Some(9) ); } #[test] //#[ignore] fn finds_a_value_in_an_array_of_even_length() { assert_eq!( find(&[1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377], 21), Some(5) ); } #[test] //#[ignore] fn identifies_that_a_value_is_not_included_in_the_array() { assert_eq!(find(&[1, 3, 4, 6, 8, 9, 11], 7), None); } #[test] //#[ignore] fn a_value_smaller_than_the_arrays_smallest_value_is_not_included() { assert_eq!(find(&[1, 3, 4, 6, 8, 9, 11], 0), None); } #[test] //#[ignore] fn a_value_larger_than_the_arrays_largest_value_is_not_included() { assert_eq!(find(&[1, 3, 4, 6, 8, 9, 11], 13), None); } #[test] //#[ignore] fn nothing_is_included_in_an_empty_array() { assert_eq!(find(&[], 1), None); } #[test] //#[ignore] #[cfg(feature = "generic")] fn works_for_arrays() { assert_eq!(find([6], 6), Some(0)); } #[test] //#[ignore] #[cfg(feature = "generic")] fn works_for_vec() { let vector = vec![6]; assert_eq!(find(&vector, 6), Some(0)); assert_eq!(find(vector, 6), Some(0)); } #[test] //#[ignore] #[cfg(feature = "generic")] fn works_for_str_elements() { assert_eq!(find(["a"], "a"), Some(0)); assert_eq!(find(["a", "b"], "b"), Some(1)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::cmp::Ordering; pub fn find<C, T>(elements: C, needle: T) -> Option<usize> where C: AsRef<[T]>, T: Ord, { let mut base = 0usize; let mut slice: &[T] = elements.as_ref(); loop { let (head, tail) = slice.split_at(slice.len() >> 1); if let Some(middle_element) = tail.first() { match middle_element.cmp(&needle) { Ordering::Less => { base += head.len() + 1; slice = &tail[1..]; } Ordering::Greater => slice = head, Ordering::Equal => { return Some(base + head.len()); } } } else { return None; } } } #}
填充/相关
Robot Simulator
1. Readme
机器人模拟器
编写机器人模拟器。
机器人工厂的测试设施需要一个程序,来验证机器人的运动。
机器人有三种可能的运动:
- 右转
- 左转
- 前进
机器人被放置在一个假设的无限网格上,以一组{x,y}坐标,例如{3,8}面向特定方向(北、东、南或西),能向北和东前进。
然后,机器人接收许多指令,测试设备验证机器人的新位置以及指向哪个方向。
- 字母串”RAALAR”的意思是:
- 右转
- 前两次
- 向左拐
- 前一次
- 再次左转
- 假设一个机器人从{ 7, 3 }向北开始,然后运行这个指令流,它应该就放在面向西方的{ 9, 4 }上。
源
灵感来自一个著名公司的面试问题.
2. 开始你的表演
// The code below is a stub. Just enough to satisfy the compiler. // In order to pass the tests you can add-to or change any of this code. #[derive(PartialEq, Debug)] pub enum Direction { North, East, South, West, } pub struct Robot; impl Robot { pub fn new(x: i32, y: i32, d: Direction) -> Self { unimplemented!("Create a robot at (x, y) ({}, {}) facing {:?}", x, y, d,) } pub fn turn_right(self) -> Self { unimplemented!() } pub fn turn_left(self) -> Self { unimplemented!() } pub fn advance(self) -> Self { unimplemented!() } pub fn instructions(self, instructions: &str) -> Self { unimplemented!( "Follow the given sequence of instructions: {}", instructions ) } pub fn position(&self) -> (i32, i32) { unimplemented!() } pub fn direction(&self) -> &Direction { unimplemented!() } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn robots_are_created_with_position_and_direction() { let robot = Robot::new(0, 0, Direction::North); assert_eq!((0, 0), robot.position()); assert_eq!(&Direction::North, robot.direction()); } #[test] //#[ignore] fn positions_can_be_negative() { let robot = Robot::new(-1, -1, Direction::South); assert_eq!((-1, -1), robot.position()); assert_eq!(&Direction::South, robot.direction()); } #[test] //#[ignore] fn turning_right_does_not_change_position() { let robot = Robot::new(0, 0, Direction::North).turn_right(); assert_eq!((0, 0), robot.position()); } #[test] //#[ignore] fn turning_right_from_north_points_the_robot_east() { let robot = Robot::new(0, 0, Direction::North).turn_right(); assert_eq!(&Direction::East, robot.direction()); } #[test] //#[ignore] fn turning_right_from_east_points_the_robot_south() { let robot = Robot::new(0, 0, Direction::East).turn_right(); assert_eq!(&Direction::South, robot.direction()); } #[test] //#[ignore] fn turning_right_from_south_points_the_robot_west() { let robot = Robot::new(0, 0, Direction::South).turn_right(); assert_eq!(&Direction::West, robot.direction()); } #[test] //#[ignore] fn turning_right_from_west_points_the_robot_north() { let robot = Robot::new(0, 0, Direction::West).turn_right(); assert_eq!(&Direction::North, robot.direction()); } #[test] //#[ignore] fn turning_left_does_not_change_position() { let robot = Robot::new(0, 0, Direction::North).turn_left(); assert_eq!((0, 0), robot.position()); } #[test] //#[ignore] fn turning_left_from_north_points_the_robot_west() { let robot = Robot::new(0, 0, Direction::North).turn_left(); assert_eq!(&Direction::West, robot.direction()); } #[test] //#[ignore] fn turning_left_from_west_points_the_robot_south() { let robot = Robot::new(0, 0, Direction::West).turn_left(); assert_eq!(&Direction::South, robot.direction()); } #[test] //#[ignore] fn turning_left_from_south_points_the_robot_east() { let robot = Robot::new(0, 0, Direction::South).turn_left(); assert_eq!(&Direction::East, robot.direction()); } #[test] //#[ignore] fn turning_left_from_east_points_the_robot_north() { let robot = Robot::new(0, 0, Direction::East).turn_left(); assert_eq!(&Direction::North, robot.direction()); } #[test] //#[ignore] fn advance_does_not_change_the_direction() { let robot = Robot::new(0, 0, Direction::North).advance(); assert_eq!(&Direction::North, robot.direction()); } #[test] //#[ignore] fn advance_increases_the_y_coordinate_by_one_when_facing_north() { let robot = Robot::new(0, 0, Direction::North).advance(); assert_eq!((0, 1), robot.position()); } #[test] //#[ignore] fn advance_decreases_the_y_coordinate_by_one_when_facing_south() { let robot = Robot::new(0, 0, Direction::South).advance(); assert_eq!((0, -1), robot.position()); } #[test] //#[ignore] fn advance_increases_the_x_coordinate_by_one_when_facing_east() { let robot = Robot::new(0, 0, Direction::East).advance(); assert_eq!((1, 0), robot.position()); } #[test] //#[ignore] fn advance_decreases_the_x_coordinate_by_one_when_facing_west() { let robot = Robot::new(0, 0, Direction::West).advance(); assert_eq!((-1, 0), robot.position()); } #[test] //#[ignore] fn follow_instructions_to_move_west_and_north() { let robot = Robot::new(0, 0, Direction::North).instructions("LAAARALA"); assert_eq!((-4, 1), robot.position()); assert_eq!(&Direction::West, robot.direction()); } #[test] //#[ignore] fn follow_instructions_to_move_west_and_south() { let robot = Robot::new(2, -7, Direction::East).instructions("RRAAAAALA"); assert_eq!((-3, -8), robot.position()); assert_eq!(&Direction::South, robot.direction()); } #[test] //#[ignore] fn follow_instructions_to_move_east_and_north() { let robot = Robot::new(8, 4, Direction::South).instructions("LAAARRRALLLL"); assert_eq!((11, 5), robot.position()); assert_eq!(&Direction::North, robot.direction()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(PartialEq, Debug, Copy, Clone)] pub enum Direction { North, East, South, West, } impl Direction { pub fn previous_clockwise(&self) -> Self { match *self { Direction::North => Direction::West, Direction::East => Direction::North, Direction::South => Direction::East, Direction::West => Direction::South, } } pub fn next_clockwise(&self) -> Self { match *self { Direction::North => Direction::East, Direction::East => Direction::South, Direction::South => Direction::West, Direction::West => Direction::North, } } } #[derive(Clone, Copy)] struct Position { x: i32, y: i32, } impl Position { fn new(x: i32, y: i32) -> Self { Position { x: x, y: y } } fn advance(&self, direction: &Direction) -> Self { match *direction { Direction::North => Self::new(self.x, self.y + 1), Direction::South => Self::new(self.x, self.y - 1), Direction::East => Self::new(self.x + 1, self.y), Direction::West => Self::new(self.x - 1, self.y), } } } #[derive(Clone)] pub struct Robot { position: Position, direction: Direction, } impl Robot { pub fn new(x: i32, y: i32, d: Direction) -> Self { Robot::build(Position::new(x, y), d) } fn build(position: Position, direction: Direction) -> Self { Robot { position: position, direction: direction, } } pub fn turn_right(&self) -> Self { Self::build(self.position, self.direction.next_clockwise()) } pub fn turn_left(&self) -> Self { Self::build(self.position, self.direction.previous_clockwise()) } pub fn advance(&self) -> Self { Self::build(self.position.advance(&self.direction), self.direction) } pub fn instructions(&self, instructions: &str) -> Self { instructions .chars() .fold(self.clone(), |robot, instruction| { robot.execute(instruction) }) } pub fn position(&self) -> (i32, i32) { (self.position.x, self.position.y) } pub fn direction(&self) -> &Direction { &self.direction } fn execute(self, command: char) -> Self { match command { 'R' => self.turn_right(), 'L' => self.turn_left(), 'A' => self.advance(), _ => self, } } } #}
填充/相关
Bracket Push
1. Readme
括号配套
给定包含括号[]
大括号{}
括号()
或它们的任何组合的字符串,验证任何和所有对,都被正确地匹配和嵌套。
资源
Ginna Baker
2. 开始你的表演
pub fn brackets_are_balanced(string: &str) -> bool { unimplemented!("Check if the string \"{}\" contains balanced brackets", string); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn paired_square_brackets() { assert!(brackets_are_balanced("[]")); } #[test] //#[ignore] fn empty_string() { assert!(brackets_are_balanced("")); } #[test] //#[ignore] fn unpaired_brackets() { assert!(!brackets_are_balanced("[[")); } #[test] //#[ignore] fn wrong_ordered_brackets() { assert!(!brackets_are_balanced("}{")); } #[test] //#[ignore] fn wrong_closing_bracket() { assert!(!brackets_are_balanced("{]")); } #[test] //#[ignore] fn paired_with_whitespace() { assert!(brackets_are_balanced("{ }")); } #[test] //#[ignore] fn simple_nested_brackets() { assert!(brackets_are_balanced("{[]}")); } #[test] //#[ignore] fn several_paired_brackets() { assert!(brackets_are_balanced("{}[]")); } #[test] //#[ignore] fn paired_and_nested_brackets() { assert!(brackets_are_balanced("([{}({}[])])")); } #[test] //#[ignore] fn unopened_closing_brackets() { assert!(!brackets_are_balanced("{[)][]}")); } #[test] //#[ignore] fn unpaired_and_nested_brackets() { assert!(!brackets_are_balanced("([{])")); } #[test] //#[ignore] fn paired_and_wrong_nested_brackets() { assert!(!brackets_are_balanced("[({]})")); } #[test] //#[ignore] fn math_expression() { assert!(brackets_are_balanced("(((185 + 223.85) * 15) - 543)/2")); } #[test] //#[ignore] fn complex_latex_expression() { let input = "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \ \\end{array}\\right)"; assert!(brackets_are_balanced(input)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; pub fn brackets_are_balanced(string: &str) -> bool { Brackets::from(string).are_balanced() } struct Brackets { raw_brackets: Vec<char>, pairs: MatchingBrackets, } impl<'a> From<&'a str> for Brackets { fn from(i: &str) -> Self { Brackets::new(String::from(i), None) } } impl Brackets { fn new(s: String, pairs: Option<Vec<(char, char)>>) -> Self { let p = match pairs { Some(x) => MatchingBrackets::from(x), None => MatchingBrackets::from(vec![('[', ']'), ('{', '}'), ('(', ')')]), }; Brackets { raw_brackets: s.chars().filter(|c| p.contains(&c)).collect::<Vec<char>>(), pairs: p, } } fn are_balanced(&self) -> bool { let mut unclosed: Vec<char> = Vec::new(); for &bracket in self.raw_brackets.iter() { if let Some(last_unclosed) = unclosed.pop() { unclosed.extend(self.pairs.unmatched(last_unclosed, bracket)); } else { unclosed.push(bracket); } } unclosed.is_empty() } } struct MatchingBrackets { collection: HashMap<char, char>, } impl From<Vec<(char, char)>> for MatchingBrackets { fn from(v: Vec<(char, char)>) -> Self { MatchingBrackets { collection: v.into_iter().collect::<HashMap<char, char>>(), } } } impl MatchingBrackets { fn contains(&self, other: &char) -> bool { let known = self.collection .keys() .chain(self.collection.values()) .collect::<Vec<_>>(); known.contains(&other) } fn closer_for(&self, k: &char) -> Option<&char> { self.collection.get(k) } fn closed_by(&self, l: char, r: char) -> bool { match self.closer_for(&l) { Some(&x) => r == x, None => false, } } fn unmatched(&self, open: char, potential_close: char) -> Vec<char> { let mut ret: Vec<char> = Vec::new(); if !self.closed_by(open, potential_close) { ret.push(open); ret.push(potential_close); } ret } } #}
填充/相关
Luhn From
1. Readme
来自 Luhn
Luhn:使用 From Trait
在做这个练习之前,你应该做原始的 LuHn 练习。如果您还没有完成 Luhn,您可以通过运行命令来获得它:
exercism download --exercise=luhn --track=rust
在原始的 LuHn 练习中,您只验证字符串,但 Luhn 算法也可以应用于整数。
在本练习中,您将实现From trait,用来将字符串、strs 和无符号整数转换为执行验证的结构。
资源
The Rust track maintainers,基于原始 Luhn 练习
2. 开始你的表演
pub struct Luhn; impl Luhn { pub fn is_valid(&self) -> bool { unimplemented!("Determine if the current Luhn struct contains a valid credit card number."); } } /// Here is the example of how the From trait could be implemented /// for the &str type. Naturally, you can implement this trait /// by hand for the every other type presented in the test suite, /// but your solution will fail if a new type is presented. /// Perhaps there exists a better solution for this problem? impl<'a> From<&'a str> for Luhn { fn from(input: &'a str) -> Self { unimplemented!("From the given input '{}' create a new Luhn struct.", input); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn you_can_validate_from_a_str() { let valid = Luhn::from("046 454 286"); let invalid = Luhn::from("046 454 287"); assert!(valid.is_valid()); assert!(!invalid.is_valid()); } #[test] //#[ignore] fn you_can_validate_from_a_string() { let valid = Luhn::from(String::from("046 454 286")); let invalid = Luhn::from(String::from("046 454 287")); assert!(valid.is_valid()); assert!(!invalid.is_valid()); } #[test] //#[ignore] fn you_can_validate_from_a_u8() { let valid = Luhn::from(240u8); let invalid = Luhn::from(241u8); assert!(valid.is_valid()); assert!(!invalid.is_valid()); } #[test] //#[ignore] fn you_can_validate_from_a_u16() { let valid = Luhn::from(64_436u16); let invalid = Luhn::from(64_437u16); assert!(valid.is_valid()); assert!(!invalid.is_valid()); } #[test] //#[ignore] fn you_can_validate_from_a_u32() { let valid = Luhn::from(46_454_286u32); let invalid = Luhn::from(46_454_287u32); assert!(valid.is_valid()); assert!(!invalid.is_valid()); } #[test] //#[ignore] fn you_can_validate_from_a_u64() { let valid = Luhn::from(8273_1232_7352_0562u64); let invalid = Luhn::from(8273_1232_7352_0569u64); assert!(valid.is_valid()); assert!(!invalid.is_valid()); } #[test] //#[ignore] fn you_can_validate_from_a_usize() { let valid = Luhn::from(8273_1232_7352_0562usize); let invalid = Luhn::from(8273_1232_7352_0569usize); assert!(valid.is_valid()); assert!(!invalid.is_valid()); } #[test] //#[ignore] fn single_digit_string_is_invalid() { assert!(!Luhn::from("1").is_valid()); } #[test] //#[ignore] fn single_zero_string_is_invalid() { assert!(!Luhn::from("0").is_valid()); } #[test] //#[ignore] fn valid_canadian_sin_is_valid() { assert!(Luhn::from("046 454 286").is_valid()); } #[test] //#[ignore] fn invalid_canadian_sin_is_invalid() { assert!(!Luhn::from("046 454 287").is_valid()); } #[test] //#[ignore] fn invalid_credit_card_is_invalid() { assert!(!Luhn::from("8273 1232 7352 0569").is_valid()); } #[test] //#[ignore] fn strings_that_contain_non_digits_are_invalid() { assert!(!Luhn::from("046a 454 286").is_valid()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub struct Luhn { digits: Vec<char>, } impl Luhn { pub fn is_valid(&self) -> bool { if self.digits.iter().any(|c| c.is_alphabetic()) || self.digits.iter().count() == 1 { return false; } self.digits .iter() .filter_map(|c| c.to_digit(10)) .rev() .enumerate() .map(|(index, digit)| if index % 2 == 0 { digit } else { digit * 2 }) .map(|digit| if digit > 9 { digit - 9 } else { digit }) .sum::<u32>() % 10 == 0 } } impl From<String> for Luhn { fn from(s: String) -> Self { Luhn { digits: s.chars().collect(), } } } impl<'a> From<&'a str> for Luhn { fn from(s: &'a str) -> Self { Luhn::from(String::from(s)) } } impl From<u8> for Luhn { fn from(s: u8) -> Self { Luhn::from(s.to_string()) } } impl From<u16> for Luhn { fn from(s: u16) -> Self { Luhn::from(s.to_string()) } } impl From<u32> for Luhn { fn from(s: u32) -> Self { Luhn::from(s.to_string()) } } impl From<u64> for Luhn { fn from(s: u64) -> Self { Luhn::from(s.to_string()) } } impl From<usize> for Luhn { fn from(s: usize) -> Self { Luhn::from(s.to_string()) } } #}
填充/相关
Queen Attack
1. Readme
女王攻击
给定棋盘上的两个皇后的位置,指示在各自位置的它们,是否能互相攻击。
在象棋游戏中,女王可以攻击同一行、列或对角线上的棋子。
棋盘可以用 8 乘 8 的数组来表示。
所以如果你告诉白皇后在(2, 3)和黑皇后在(5, 6),那么设定像这样:
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
_ _ _ W _ _ _ _
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
_ _ _ _ _ _ B _
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
你也可以回答女王是否可以互相攻击。而在这种情况下,答案是肯定的,他们可以,因为这两个部分共用一个对角线。
资源
J Dalbey 的程序设计实践问题http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html
2. 开始你的表演
#[derive(Debug)] pub struct ChessPosition; #[derive(Debug)] pub struct Queen; impl ChessPosition { pub fn new(rank: i32, file: i32) -> Option<Self> { unimplemented!( "Construct a ChessPosition struct, given the following rank, file: ({}, {}). If the position is invalid return None.", rank, file ); } } impl Queen { pub fn new(position: ChessPosition) -> Self { unimplemented!( "Given the chess position {:?}, construct a Queen struct.", position ); } pub fn can_attack(&self, other: &Queen) -> bool { unimplemented!( "Determine if this Queen can attack the other Queen {:?}", other ); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn chess_position_on_the_board_is_some() { assert!(ChessPosition::new(2, 4).is_some()); } #[test] //#[ignore] fn chess_position_off_the_board_is_none() { assert!(ChessPosition::new(-1, 2).is_none()); assert!(ChessPosition::new(8, 2).is_none()); assert!(ChessPosition::new(5, -1).is_none()); assert!(ChessPosition::new(5, 8).is_none()); } #[test] //#[ignore] fn queen_is_created_with_a_valid_position() { Queen::new(ChessPosition::new(2, 4).unwrap()); } #[test] //#[ignore] fn queens_that_can_not_attack() { let white_queen = Queen::new(ChessPosition::new(2, 4).unwrap()); let black_queen = Queen::new(ChessPosition::new(6, 6).unwrap()); assert!(!white_queen.can_attack(&black_queen)); } #[test] //#[ignore] fn queens_on_the_same_rank_can_attack() { let white_queen = Queen::new(ChessPosition::new(2, 4).unwrap()); let black_queen = Queen::new(ChessPosition::new(2, 6).unwrap()); assert!(white_queen.can_attack(&black_queen)); } #[test] //#[ignore] fn queens_on_the_same_file_can_attack() { let white_queen = Queen::new(ChessPosition::new(4, 5).unwrap()); let black_queen = Queen::new(ChessPosition::new(3, 5).unwrap()); assert!(white_queen.can_attack(&black_queen)); } #[test] //#[ignore] fn queens_on_the_same_diagonal_can_attack_one() { let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap()); let black_queen = Queen::new(ChessPosition::new(0, 4).unwrap()); assert!(white_queen.can_attack(&black_queen)); } #[test] //#[ignore] fn queens_on_the_same_diagonal_can_attack_two() { let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap()); let black_queen = Queen::new(ChessPosition::new(3, 1).unwrap()); assert!(white_queen.can_attack(&black_queen)); } #[test] //#[ignore] fn queens_on_the_same_diagonal_can_attack_three() { let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap()); let black_queen = Queen::new(ChessPosition::new(1, 1).unwrap()); assert!(white_queen.can_attack(&black_queen)); } #[test] //#[ignore] fn queens_on_the_same_diagonal_can_attack_four() { let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap()); let black_queen = Queen::new(ChessPosition::new(5, 5).unwrap()); assert!(white_queen.can_attack(&black_queen)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(Debug)] pub struct Queen { position: ChessPosition, } pub trait ChessPiece { fn position(&self) -> &ChessPosition; fn can_attack<T: ChessPiece>(&self, other: &T) -> bool; } impl ChessPiece for Queen { fn position(&self) -> &ChessPosition { &self.position } fn can_attack<T: ChessPiece>(&self, piece: &T) -> bool { self.position.horizontal_from(&piece.position()) || self.position.vertical_from(&piece.position()) || self.position.diagonal_from(&piece.position()) } } impl Queen { pub fn new(position: ChessPosition) -> Queen { Queen { position: position } } } #[derive(Debug)] pub struct ChessPosition { pub rank: i8, pub file: i8, } impl ChessPosition { pub fn new(rank: i8, file: i8) -> Option<Self> { let position = ChessPosition { rank: rank, file: file, }; if position.is_valid() { Some(position) } else { None } } fn is_valid(&self) -> bool { self.rank >= 0 && self.rank <= 7 && self.file >= 0 && self.file <= 7 } fn horizontal_from(&self, other: &ChessPosition) -> bool { self.rank == other.rank } fn vertical_from(&self, other: &ChessPosition) -> bool { self.file == other.file } fn diagonal_from(&self, other: &ChessPosition) -> bool { self.sum() == other.sum() || self.difference() == other.difference() } fn sum(&self) -> i8 { self.rank + self.file } fn difference(&self) -> i8 { self.rank - self.file } } #}
填充/相关
Bowling
1. Readme
保龄球
打保龄球比赛。
保龄球是一种游戏,玩家掷出一个沉重的球来击倒排列成三角形的罐子。编写代码以跟踪保龄球比赛的得分。
保龄球得分
游戏由 10 轮组成。一轮由一个或两个球投掷组成,其中 10 个罐子处于该轮初始化状态。一个回合有三种情况。
-
开放(open)轮是轮的记录小于 10 的分数。在这种情况下,该轮的分数是被击倒数。
-
备用(spare)的是所有十个罐子被第二次投掷击倒。备用的总分是 10 加上,下一次投掷击倒的罐子数量。
-
全倒(strike)是所有十个罐子被第一次击倒。全倒的总分是 10 加上,在接下来的两次投掷击倒的罐子数量。如果进行第二次击球又触发 strike,则再扔球之前不确定第一次击球的值。
这是一个三轮的例子:
第 1 轮 | 第 2 轮 | 第 3 轮 |
---|---|---|
X(strike) | 5 /(spare) | 9 0(open) |
第 1 轮是(10 + 5 + 5)= 20
第 2 轮是(5 + 5 + 9)= 19
轮 3 是(9 + 0)= 9
这意味着当前的运行总数为 48.
游戏中的第十轮是一个特例。如果有人投掷全倒或备用,那么他们会得到一个补球。补球会计入第 10 轮的总和。在补球上获得一次全倒或备用不会给球员带来更多的补球。第 10 轮的总分是被击倒的罐子总数。
对于 X1 /(全倒和备用)的第十轮,总分为 20。
对于 XXX (三次全倒)的第十轮,总分为 30。
要求
编写代码以跟踪保龄球比赛的得分。它应该支持两个操作:
roll(pins : int)
每次玩家滚球时都会调用。这个参数是被击倒的罐子数量.score() : int
仅在游戏结束时才会被调用。它返回该游戏的总分。
资源
保龄球比赛, Kata 的 UncleBobhttp://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata
2. 开始你的表演
#[derive(Debug, PartialEq)] pub enum Error { NotEnoughPinsLeft, GameComplete, } pub struct BowlingGame {} impl BowlingGame { pub fn new() -> Self { unimplemented!(); } pub fn roll(&mut self, pins: u16) -> Result<(), Error> { unimplemented!("Record that {} pins have been scored", pins); } pub fn score(&self) -> Option<u16> { unimplemented!("Return the score if the game is complete, or None if not."); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn roll_returns_a_result() { let mut game = BowlingGame::new(); assert!(game.roll(0).is_ok()); } #[test] //#[ignore] fn you_can_not_roll_more_than_ten_pins_in_a_single_roll() { let mut game = BowlingGame::new(); assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft));; } #[test] //#[ignore] fn a_game_score_is_some_if_ten_frames_have_been_rolled() { let mut game = BowlingGame::new(); for _ in 0..10 { let _ = game.roll(0); let _ = game.roll(0); } assert!(game.score().is_some()); } #[test] //#[ignore] fn you_can_not_score_a_game_with_no_rolls() { let game = BowlingGame::new(); assert_eq!(game.score(), None); } #[test] //#[ignore] fn a_game_score_is_none_if_fewer_than_ten_frames_have_been_rolled() { let mut game = BowlingGame::new(); for _ in 0..9 { let _ = game.roll(0); let _ = game.roll(0); } assert_eq!(game.score(), None); } #[test] //#[ignore] fn a_roll_is_err_if_the_game_is_done() { let mut game = BowlingGame::new(); for _ in 0..10 { let _ = game.roll(0); let _ = game.roll(0); } assert_eq!(game.roll(0), Err(Error::GameComplete));; } #[test] //#[ignore] fn twenty_zero_pin_rolls_scores_zero() { let mut game = BowlingGame::new(); for _ in 0..20 { let _ = game.roll(0); } assert_eq!(game.score(), Some(0)); } #[test] //#[ignore] fn ten_frames_without_a_strike_or_spare() { let mut game = BowlingGame::new(); for _ in 0..10 { let _ = game.roll(3); let _ = game.roll(6); } assert_eq!(game.score(), Some(90)); } #[test] //#[ignore] fn spare_in_the_first_frame_followed_by_zeros() { let mut game = BowlingGame::new(); let _ = game.roll(6); let _ = game.roll(4); for _ in 0..18 { let _ = game.roll(0); } assert_eq!(game.score(), Some(10)); } #[test] //#[ignore] fn points_scored_in_the_roll_after_a_spare_are_counted_twice_as_a_bonus() { let mut game = BowlingGame::new(); let _ = game.roll(6); let _ = game.roll(4); let _ = game.roll(3); for _ in 0..17 { let _ = game.roll(0); } assert_eq!(game.score(), Some(16)); } #[test] //#[ignore] fn consecutive_spares_each_get_a_one_roll_bonus() { let mut game = BowlingGame::new(); let _ = game.roll(5); let _ = game.roll(5); let _ = game.roll(3); let _ = game.roll(7); let _ = game.roll(4); for _ in 0..15 { let _ = game.roll(0); } assert_eq!(game.score(), Some(31)); } #[test] //#[ignore] fn if_the_last_frame_is_a_spare_you_get_one_extra_roll_that_is_scored_once() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(5); let _ = game.roll(5); let _ = game.roll(7); assert_eq!(game.score(), Some(17)); } #[test] //#[ignore] fn a_strike_earns_ten_points_in_a_frame_with_a_single_roll() { let mut game = BowlingGame::new(); let _ = game.roll(10); for _ in 0..18 { let _ = game.roll(0); } assert_eq!(game.score(), Some(10)); } #[test] //#[ignore] fn points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus() { let mut game = BowlingGame::new(); let _ = game.roll(10); let _ = game.roll(5); let _ = game.roll(3); for _ in 0..16 { let _ = game.roll(0); } assert_eq!(game.score(), Some(26)); } #[test] //#[ignore] fn consecutive_strikes_each_get_the_two_roll_bonus() { let mut game = BowlingGame::new(); let _ = game.roll(10); let _ = game.roll(10); let _ = game.roll(10); let _ = game.roll(5); let _ = game.roll(3); for _ in 0..12 { let _ = game.roll(0); } assert_eq!(game.score(), Some(81)); } #[test] //#[ignore] fn a_strike_in_the_last_frame_earns_a_two_roll_bonus_that_is_counted_once() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); let _ = game.roll(7); let _ = game.roll(1); assert_eq!(game.score(), Some(18)); } #[test] //#[ignore] fn a_spare_with_the_two_roll_bonus_does_not_get_a_bonus_roll() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); let _ = game.roll(7); let _ = game.roll(3); assert_eq!(game.score(), Some(20)); } #[test] //#[ignore] fn strikes_with_the_two_roll_bonus_do_not_get_a_bonus_roll() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); let _ = game.roll(10); let _ = game.roll(10); assert_eq!(game.score(), Some(30)); } #[test] //#[ignore] fn a_strike_with_the_one_roll_bonus_after_a_spare_in_the_last_frame_does_not_get_a_bonus() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(7); let _ = game.roll(3); let _ = game.roll(10); assert_eq!(game.score(), Some(20)); } #[test] //#[ignore] fn all_strikes_is_a_perfect_score_of_300() { let mut game = BowlingGame::new(); for _ in 0..12 { let _ = game.roll(10); } assert_eq!(game.score(), Some(300)); } #[test] //#[ignore] fn you_can_not_roll_more_than_ten_pins_in_a_single_frame() { let mut game = BowlingGame::new(); assert!(game.roll(5).is_ok()); assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft));; } #[test] //#[ignore] fn first_bonus_ball_after_a_final_strike_can_not_score_an_invalid_number_of_pins() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft));; } #[test] //#[ignore] fn the_two_balls_after_a_final_strike_can_not_score_an_invalid_number_of_pins() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); assert!(game.roll(5).is_ok()); assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft));; } #[test] //#[ignore] fn the_two_balls_after_a_final_strike_can_be_a_strike_and_non_strike() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); assert!(game.roll(10).is_ok()); assert!(game.roll(6).is_ok()); } #[test] //#[ignore] fn the_two_balls_after_a_final_strike_can_not_be_a_non_strike_followed_by_a_strike() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); assert!(game.roll(6).is_ok()); assert_eq!(game.roll(10), Err(Error::NotEnoughPinsLeft));; } #[test] //#[ignore] fn second_bonus_ball_after_a_final_strike_can_not_score_an_invalid_number_of_pins_even_if_first_is_strike( ) { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); assert!(game.roll(10).is_ok()); assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft));; } #[test] //#[ignore] fn if_the_last_frame_is_a_strike_you_can_not_score_before_the_extra_rolls_are_taken() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(10); assert_eq!(game.score(), None); let _ = game.roll(10); assert_eq!(game.score(), None); let _ = game.roll(10); assert!(game.score().is_some()); } #[test] //#[ignore] fn if_the_last_frame_is_a_spare_you_can_not_create_a_score_before_extra_roll_is_taken() { let mut game = BowlingGame::new(); for _ in 0..18 { let _ = game.roll(0); } let _ = game.roll(5); let _ = game.roll(5); assert_eq!(game.score(), None); let _ = game.roll(10); assert!(game.score().is_some()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(Debug, PartialEq)] pub enum Error { NotEnoughPinsLeft, GameComplete, } pub struct BowlingGame { frames: Vec<Frame>, } struct Frame { rolls: Vec<u16>, bonus: Vec<u16>, } impl Frame { fn score(&self) -> u16 { self.roll_score() + self.bonus_score() } fn roll_score(&self) -> u16 { self.rolls.iter().sum() } fn bonus_score(&self) -> u16 { self.bonus.iter().sum() } fn is_valid(&self) -> bool { self.rolls_valid() && self.bonus_valid() } fn rolls_valid(&self) -> bool { self.roll_score() <= 10 } fn bonus_valid(&self) -> bool { if self.is_open() || !self.bonus_done() { return true; } if self.is_spare() { return self.bonus_score() <= 10; } if let Some(first) = self.bonus.iter().next() { if *first == 10 { self.bonus_score() <= 20 } else { self.bonus_score() <= 10 } } else { unreachable!(); } } fn is_complete(&self) -> bool { self.is_open() || self.bonus_done() } fn rolls_done(&self) -> bool { self.rolls.len() == 2 || self.is_strike() } fn bonus_done(&self) -> bool { (self.is_spare() && self.bonus.len() == 1) || (self.is_strike() && self.bonus.len() == 2) } fn is_open(&self) -> bool { self.rolls.len() == 2 && self.roll_score() < 10 } fn is_spare(&self) -> bool { self.rolls.len() == 2 && self.roll_score() == 10 } fn is_strike(&self) -> bool { self.rolls.len() == 1 && self.roll_score() == 10 } fn add_roll(&mut self, roll: u16) { if !self.is_complete() { if self.is_spare() || self.is_strike() { self.bonus.push(roll) } else { self.rolls.push(roll) } } } fn new() -> Self { Frame { rolls: vec![], bonus: vec![], } } } impl BowlingGame { pub fn new() -> Self { BowlingGame { frames: vec![Frame::new()], } } pub fn roll(&mut self, pins: u16) -> Result<(), Error> { if pins > 10 { Err(Error::NotEnoughPinsLeft) } else { if self.score().is_some() { return Err(Error::GameComplete); } for frame in self.frames.iter_mut() { frame.add_roll(pins) } if self.frames.iter().any(|f| !f.is_valid()) { return Err(Error::NotEnoughPinsLeft); } if self.frames.iter().last().unwrap().rolls_done() && self.frames.len() < 10 { self.frames.push(Frame::new()); } Ok(()) } } pub fn score(&self) -> Option<u16> { if !self.is_done() { None } else { Some(self.frames.iter().fold(0, |acc, r| acc + r.score())) } } fn is_done(&self) -> bool { self.frames.len() == 10 && self.frames.iter().all(|f| f.is_complete()) } } #}
填充/相关
Sublist
1. Readme
子列表
给定两个列表确定第一个列表是否包含在第二个列表中;第二个列表是否包含在第一个列表中;两个列表是否包含在彼此,或者这些都不是真的。
具体来说,列表 A 是列表 B 的子列表,在于是否 B 的前面删除 0 个或更多元素和 B 的后面删除 0 个或更多元素,则得到完全等于 A 的列表。
例子:
- A =
[1,2,3]
,B =[1,2,3,4,5]
,A 是 B 的子列表 - A =
[3,4,5]
,B =[1,2,3,4,5]
,A 是 B 的子列表 - A =
[3,4]
,B =[1,2,3,4,5]
,A 是 B 的子列表 - A =
[1,2,3]
,B =[1,2,3]
,A 等于 B。 - A =
[1,2,3,4,5]
,B =[2,3,4]
,A 是 B 的父列表 - A =
[1,2,4]
,B =[1,2,3,4,5]
,A 不是 B 的父列表,子列表,或等于 B 的列表
2. 开始你的表演
#[derive(Debug, PartialEq)] pub enum Comparison { Equal, Sublist, Superlist, Unequal, } pub fn sublist<T: PartialEq>(_first_list: &[T], _second_list: &[T]) -> Comparison { unimplemented!("Determine if the first list is equal to, sublist of, superlist of or unequal to the second list."); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn empty_equals_empty() { let v: &[u32] = &[]; assert_eq!(Comparison::Equal, sublist(&v, &v)); } #[test] //#[ignore] fn test_empty_is_a_sublist_of_anything() { assert_eq!(Comparison::Sublist, sublist(&[], &['a', 's', 'd', 'f'])); } #[test] //#[ignore] fn test_anything_is_a_superlist_of_empty() { assert_eq!(Comparison::Superlist, sublist(&['a', 's', 'd', 'f'], &[])); } #[test] //#[ignore] fn test_1_is_not_2() { assert_eq!(Comparison::Unequal, sublist(&[1], &[2])); } #[test] //#[ignore] fn test_compare_larger_equal_lists() { use std::iter::repeat; let v: Vec<char> = repeat('x').take(1000).collect(); assert_eq!(Comparison::Equal, sublist(&v, &v)); } #[test] //#[ignore] fn test_sublist_at_start() { assert_eq!(Comparison::Sublist, sublist(&[1, 2, 3], &[1, 2, 3, 4, 5])); } #[test] //#[ignore] fn sublist_in_middle() { assert_eq!(Comparison::Sublist, sublist(&[4, 3, 2], &[5, 4, 3, 2, 1])); } #[test] //#[ignore] fn sublist_at_end() { assert_eq!(Comparison::Sublist, sublist(&[3, 4, 5], &[1, 2, 3, 4, 5])); } #[test] //#[ignore] fn partially_matching_sublist_at_start() { assert_eq!(Comparison::Sublist, sublist(&[1, 1, 2], &[1, 1, 1, 2])); } #[test] //#[ignore] fn sublist_early_in_huge_list() { let huge: Vec<u32> = (1..1000000).collect(); assert_eq!(Comparison::Sublist, sublist(&[3, 4, 5], &huge)); } #[test] //#[ignore] fn huge_sublist_not_in_huge_list() { let v1: Vec<u64> = (10..1000001).collect(); let v2: Vec<u64> = (1..1000000).collect(); assert_eq!(Comparison::Unequal, sublist(&v1, &v2)); } #[test] //#[ignore] fn superlist_at_start() { assert_eq!(Comparison::Superlist, sublist(&[1, 2, 3, 4, 5], &[1, 2, 3])); } #[test] //#[ignore] fn superlist_in_middle() { assert_eq!(Comparison::Superlist, sublist(&[5, 4, 3, 2, 1], &[4, 3, 2])); } #[test] //#[ignore] fn superlist_at_end() { assert_eq!(Comparison::Superlist, sublist(&[1, 2, 3, 4, 5], &[3, 4, 5])); } #[test] //#[ignore] fn superlist_early_in_huge_list() { let huge: Vec<u32> = (1..1000000).collect(); assert_eq!(Comparison::Superlist, sublist(&huge, &[3, 4, 5])); } #[test] //#[ignore] fn recurring_values_sublist() { assert_eq!( Comparison::Sublist, sublist(&[1, 2, 1, 2, 3], &[1, 2, 3, 1, 2, 1, 2, 3, 2, 1]) ); } #[test] //#[ignore] fn recurring_values_unequal() { assert_eq!( Comparison::Unequal, sublist(&[1, 2, 1, 2, 3], &[1, 2, 3, 1, 2, 3, 2, 3, 2, 1]) ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(PartialEq, Eq, Debug)] pub enum Comparison { Equal, Sublist, Superlist, Unequal, } pub fn sublist<T: PartialEq>(a: &[T], b: &[T]) -> Comparison { if a == b { Comparison::Equal } else if contains(a, b) { Comparison::Superlist } else if contains(b, a) { Comparison::Sublist } else { Comparison::Unequal } } fn contains<T: PartialEq>(a: &[T], b: &[T]) -> bool { if a.len() < b.len() { return false; } if a.starts_with(b) { return true; } contains(&a[1..], b) } #}
填充/相关
Space Age
1. Readme
地球年
给定年龄(以秒为单位),计算某人的年龄:
- 地球:轨道周期 365.25 地球日,或 31557600 秒
- 水星:轨道周期 0.2408467 地球年
- 金星:轨道周期 0.61519726 地球年
- 火星:轨道周期 1.8808158 地球年
- 木星:轨道周期 11.862615 地球年
- 土星:轨道周期 29.447498 地球年
- 天王星:轨道周期 84.016846 地球年
- 海王星:轨道周期 164.79132 地球年
因此,如果你被告知某人的年龄为 1,000,000,000 秒,你应该可以说它们的年龄为 31.69 地球年.
如果你想知道为什么冥王星没有加入进来,请去观看this youtube video.
主题
在解决此问题时,您可能想要阅读的一些 Rust 主题:
- Trait,用 From trait 实现,或实现自己的 trait
- trait 的默认方法实现
资源
部分灵感来自 Chris Pine 在线学习编程教程的第 1 章.http://pine.fm/LearnToProgram/?Chapter=01
2. 开始你的表演
// The code below is a stub. Just enough to satisfy the compiler. // In order to pass the tests you can add-to or change any of this code. #[derive(Debug)] pub struct Duration; impl From<u64> for Duration { fn from(s: u64) -> Self { unimplemented!("s, measured in seconds: {}", s) } } pub trait Planet { fn years_during(d: &Duration) -> f64 { unimplemented!( "convert a duration ({:?}) to the number of years on this planet for that duration", d, ); } } pub struct Mercury; pub struct Venus; pub struct Earth; pub struct Mars; pub struct Jupiter; pub struct Saturn; pub struct Uranus; pub struct Neptune; impl Planet for Mercury {} impl Planet for Venus {} impl Planet for Earth {} impl Planet for Mars {} impl Planet for Jupiter {} impl Planet for Saturn {} impl Planet for Uranus {} impl Planet for Neptune {}
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn assert_in_delta(expected: f64, actual: f64) { let diff: f64 = (expected - actual).abs(); let delta: f64 = 0.01; if diff > delta { panic!( "Your result of {} should be within {} of the expected result {}", actual, delta, expected ) } } #[test] fn earth_age() { let duration = Duration::from(1_000_000_000); assert_in_delta(31.69, Earth::years_during(&duration)); } #[test] //#[ignore] fn mercury_age() { let duration = Duration::from(2_134_835_688); assert_in_delta(280.88, Mercury::years_during(&duration)); } #[test] //#[ignore] fn venus_age() { let duration = Duration::from(189_839_836); assert_in_delta(9.78, Venus::years_during(&duration)); } #[test] //#[ignore] fn mars_age() { let duration = Duration::from(2_329_871_239); assert_in_delta(39.25, Mars::years_during(&duration)); } #[test] //#[ignore] fn jupiter_age() { let duration = Duration::from(901_876_382); assert_in_delta(2.41, Jupiter::years_during(&duration)); } #[test] //#[ignore] fn saturn_age() { let duration = Duration::from(3_000_000_000); assert_in_delta(3.23, Saturn::years_during(&duration)); } #[test] //#[ignore] fn uranus_age() { let duration = Duration::from(3_210_123_456); assert_in_delta(1.21, Uranus::years_during(&duration)); } #[test] //#[ignore] fn neptune_age() { let duration = Duration::from(8_210_123_456); assert_in_delta(1.58, Neptune::years_during(&duration)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub struct Duration { seconds: f64, } impl From<u64> for Duration { fn from(s: u64) -> Self { Duration { seconds: s as f64 } } } impl From<f64> for Duration { fn from(s: f64) -> Self { Duration { seconds: s } } } pub trait Planet { fn orbital_duration() -> Duration; fn years_during(d: &Duration) -> f64 { d.seconds / Self::orbital_duration().seconds } } pub struct Mercury; pub struct Venus; pub struct Earth; pub struct Mars; pub struct Jupiter; pub struct Saturn; pub struct Uranus; pub struct Neptune; impl Planet for Mercury { fn orbital_duration() -> Duration { Duration::from(7600543.81992) } } impl Planet for Venus { fn orbital_duration() -> Duration { Duration::from(19414149.052176) } } impl Planet for Earth { fn orbital_duration() -> Duration { Duration::from(31557600) } } impl Planet for Mars { fn orbital_duration() -> Duration { Duration::from(59354032.69008) } } impl Planet for Jupiter { fn orbital_duration() -> Duration { Duration::from(374355659.124) } } impl Planet for Saturn { fn orbital_duration() -> Duration { Duration::from(929292362.8848) } } impl Planet for Uranus { fn orbital_duration() -> Duration { Duration::from(2651370019.3296) } } impl Planet for Neptune { fn orbital_duration() -> Duration { Duration::from(5200418560.032) } } #}
填充/相关
Luhn Trait
1. Readme
鲁恩 trait
Luhn: 使用自定义 trait
在做这个练习之前,你应该做原始的 luhn 练习和进阶练习,”luhn:使用 From trait”.
要获得原始的 Luhn 练习,运行
exercism download --exercise=luhn --track=rust
要获得”Lunn:使用 From trait”的练习,运行
exercism download --exercise=luhn-from --track=rust
在原始的 luhn 练习中,您只验证字符串,但 luhn 算法也可以应用于整数。
在”Lurn:使用 From trait”中,你实现了一个 From trait,还需要你创建一个 luhn 结构。
要是不创建一个结构来执行验证呢,如果要您自己验证了原语本身(即,String,u8 等)呢?
在本练习中,您将创建并实现自定义trait,来执行验证.
注:它是实现原语的 trait,不是 Rust 的习惯。 在这个练习中,我们展示了一些你可以做的东西,而不是你应该做的东西。如果你发现自己在原语上实现了 trait,也许你有一个Primitive Obsession例子。
资源
基于原始 luhn 练习,延续 Rust 轨迹
2. 开始你的表演
pub trait Luhn { fn valid_luhn(&self) -> bool; } /// Here is the example of how to implement custom Luhn trait /// for the &str type. Naturally, you can implement this trait /// by hand for the every other type presented in the test suite, /// but your solution will fail if a new type is presented. /// Perhaps there exists a better solution for this problem? impl<'a> Luhn for &'a str { fn valid_luhn(&self) -> bool { unimplemented!("Determine if '{}' is a valid credit card number.", self); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn you_can_validate_from_a_str() { assert!("046 454 286".valid_luhn()); assert!(!"046 454 287".valid_luhn()); } #[test] //#[ignore] fn you_can_validate_from_a_string() { assert!(String::from("046 454 286").valid_luhn()); assert!(!String::from("046 454 287").valid_luhn()); } #[test] //#[ignore] fn you_can_validate_from_a_u8() { assert!(240u8.valid_luhn()); assert!(!241u8.valid_luhn()); } #[test] //#[ignore] fn you_can_validate_from_a_u16() { let valid = 64_436u16; let invalid = 64_437u16; assert!(valid.valid_luhn()); assert!(!invalid.valid_luhn()); } #[test] //#[ignore] fn you_can_validate_from_a_u32() { let valid = 46_454_286u32; let invalid = 46_454_287u32; assert!(valid.valid_luhn()); assert!(!invalid.valid_luhn()); } #[test] //#[ignore] fn you_can_validate_from_a_u64() { let valid = 8273_1232_7352_0562u64; let invalid = 8273_1232_7352_0569u64; assert!(valid.valid_luhn()); assert!(!invalid.valid_luhn()); } #[test] //#[ignore] fn you_can_validate_from_a_usize() { let valid = 8273_1232_7352_0562usize; let invalid = 8273_1232_7352_0569usize; assert!(valid.valid_luhn()); assert!(!invalid.valid_luhn()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub trait Luhn { fn valid_luhn(&self) -> bool; } impl Luhn for String { fn valid_luhn(&self) -> bool { if self.chars().any(|c| c.is_alphabetic()) || self.chars().count() == 1 { return false; } self.chars() .filter_map(|c| c.to_digit(10)) .rev() .enumerate() .map(|(index, digit)| if index % 2 == 0 { digit } else { digit * 2 }) .map(|digit| if digit > 9 { digit - 9 } else { digit }) .sum::<u32>() % 10 == 0 } } impl<'a> Luhn for &'a str { fn valid_luhn(&self) -> bool { String::from(*self).valid_luhn() } } impl Luhn for u8 { fn valid_luhn(&self) -> bool { self.to_string().valid_luhn() } } impl Luhn for u16 { fn valid_luhn(&self) -> bool { self.to_string().valid_luhn() } } impl Luhn for u32 { fn valid_luhn(&self) -> bool { self.to_string().valid_luhn() } } impl Luhn for u64 { fn valid_luhn(&self) -> bool { self.to_string().valid_luhn() } } impl Luhn for usize { fn valid_luhn(&self) -> bool { self.to_string().valid_luhn() } } #}
填充/相关
Macros
1. Readme
宏
宏是 Rust 程序员工具箱中的一个强大工具,要想对它有个简单认知,可以看看宏例子。让我们写一个!
问题陈述
你可以用vec![]
内联宏,生产一个任意长度的Vec
。然而,Rust 并没有生产HashMap
的内联宏hashmap!()
。
例如,您的库的用户可能会编写hashmap!('a' => 3, 'b' => 11, 'z' => 32)
。这应该扩展到以下代码:
# #![allow(unused_variables)] #fn main() { { let mut hm = HashMap::new(); hm.insert('a', 3); hm.insert('b', 11); hm.insert('z', 32); hm } #}
注意maplit
箱子时提供了一个很好地解决这个问题的宏。但请执行您自己的解决方案,而不是使用这个箱子,请在查看箱子源代码之前,自己尝试下。
资源
Peter Goodspeed-Niklaus
2. 开始你的表演
#[macro_export] macro_rules! hashmap { () => { unimplemented!() }; }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[macro_use] use std::collections::HashMap; #[test] fn test_empty() { let expected: HashMap<u32, u32> = HashMap::new(); let computed: HashMap<u32, u32> = hashmap!(); assert_eq!(computed, expected); } #[test] //#[ignore] fn test_no_trailing_comma() { let mut expected = HashMap::new(); expected.insert(1, "one"); expected.insert(2, "two"); assert_eq!(hashmap!(1 => "one", 2 => "two"), expected); } #[test] //#[ignore] fn test_trailing_comma() { let mut expected = HashMap::new(); expected.insert('h', 89); expected.insert('a', 1); expected.insert('s', 19); expected.insert('h', 8); assert_eq!( hashmap!( 'h' => 89, 'a' => 1, 's' => 19, 'h' => 8, ), expected ); } #[test] //#[ignore] fn test_nested() { let mut expected = HashMap::new(); expected.insert("non-empty", { let mut subhashmap = HashMap::new(); subhashmap.insert(23, 623); subhashmap.insert(34, 21); subhashmap }); expected.insert("empty", HashMap::new()); assert_eq!( hashmap!( "non-empty" => hashmap!( 23 => 623, 34 => 21 ), "empty" => hashmap!() ), expected ); } mod test { #[test] //#[ignore] fn type_not_in_scope() { let _expected: ::std::collections::HashMap<(), ()> = hashmap!(); } } #}
4. 答案
# #![allow(unused_variables)] #fn main() { // Ignoring the README's injunction, this is heavily based on the implementation from maplit. // Original source is at https://github.com/bluss/maplit/blob/master/src/lib.rs#L27-L60 #[macro_export] macro_rules! hashmap { ($($key:expr => $value:expr,)+) => { hashmap!($($key => $value),+) }; ($($key:expr => $value:expr),*) => { { let mut _map = ::std::collections::HashMap::new(); $( _map.insert($key, $value); )* _map } }; } #}
填充/相关
Allergies
1. Readme
过敏
给出一个人的过敏分,确定他们是否对某一物品过敏,以及他们的过敏列表.
过敏测试产生单个数字分数,其中包含有关该人所有过敏的信息(他们进行了测试)。
测试的物品列表(及其值)为:
- 鸡蛋(1)
- 花生(2)
- 贝类(4)
- 草莓(8)
- 西红柿(16)
- 巧克力(32)
- 花粉(64)
- 猫(128)
因此,如果汤姆对花生和巧克力过敏,他会得到 34 分.
现在,只要得到 34 分,你的程序应该说:
- 汤姆是否对上面列出的任何一种过敏原过敏.
- 汤姆所有过敏原。
注意:给出的分数可能不包括上面列出的过敏原(即分数为 256,512,1024 等的过敏原)。您的程序应忽略这些组成部分。例如,如果过敏分数是 257,您的程序应该只报告鸡蛋(1)过敏。
资源
Jumpstart 实验室热身http://jumpstartlab.com
2. 开始你的表演
pub struct Allergies; #[derive(Debug, PartialEq)] pub enum Allergen { Eggs, Peanuts, Shellfish, Strawberries, Tomatoes, Chocolate, Pollen, Cats, } impl Allergies { pub fn new(score: u32) -> Self { unimplemented!( "Given the '{}' score, construct a new Allergies struct.", score ); } pub fn is_allergic_to(&self, allergen: &Allergen) -> bool { unimplemented!( "Determine if the patient is allergic to the '{:?}' allergen.", allergen ); } pub fn allergies(&self) -> Vec<Allergen> { unimplemented!("Return the list of allergens contained within the score with which the Allergies struct was made."); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn compare_allergy_vectors(expected: &[Allergen], actual: &[Allergen]) { for element in expected { if !actual.contains(element) { panic!( "Allergen missing\n {:?} should be in {:?}", element, actual ); } } if actual.len() != expected.len() { panic!( "Allergy vectors are of different lengths\n expected {:?}\n got {:?}", expected, actual ); } } #[test] fn is_not_allergic_to_anything() { let allergies = Allergies::new(0); assert!(!allergies.is_allergic_to(&Allergen::Peanuts)); assert!(!allergies.is_allergic_to(&Allergen::Cats)); assert!(!allergies.is_allergic_to(&Allergen::Strawberries)); } #[test] //#[ignore] fn is_allergic_to_eggs() { assert!(Allergies::new(1).is_allergic_to(&Allergen::Eggs)); } #[test] //#[ignore] fn is_allergic_to_egg_shellfish_and_strawberries() { let allergies = Allergies::new(5); assert!(allergies.is_allergic_to(&Allergen::Eggs)); assert!(allergies.is_allergic_to(&Allergen::Shellfish)); assert!(!allergies.is_allergic_to(&Allergen::Strawberries)); } #[test] //#[ignore] fn no_allergies_at_all() { let expected = &[]; let allergies = Allergies::new(0).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn allergic_to_just_eggs() { let expected = &[Allergen::Eggs]; let allergies = Allergies::new(1).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn allergic_to_just_peanuts() { let expected = &[Allergen::Peanuts]; let allergies = Allergies::new(2).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn allergic_to_just_strawberries() { let expected = &[Allergen::Strawberries]; let allergies = Allergies::new(8).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn allergic_to_eggs_and_peanuts() { let expected = &[Allergen::Eggs, Allergen::Peanuts]; let allergies = Allergies::new(3).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn allergic_to_eggs_and_shellfish() { let expected = &[Allergen::Eggs, Allergen::Shellfish]; let allergies = Allergies::new(5).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn allergic_to_many_things() { let expected = &[ Allergen::Strawberries, Allergen::Tomatoes, Allergen::Chocolate, Allergen::Pollen, Allergen::Cats, ]; let allergies = Allergies::new(248).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn allergic_to_everything() { let expected = &[ Allergen::Eggs, Allergen::Peanuts, Allergen::Shellfish, Allergen::Strawberries, Allergen::Tomatoes, Allergen::Chocolate, Allergen::Pollen, Allergen::Cats, ]; let allergies = Allergies::new(255).allergies(); compare_allergy_vectors(expected, &allergies); } #[test] //#[ignore] fn scores_over_255_do_not_trigger_false_positives() { let expected = &[ Allergen::Eggs, Allergen::Shellfish, Allergen::Strawberries, Allergen::Tomatoes, Allergen::Chocolate, Allergen::Pollen, Allergen::Cats, ]; let allergies = Allergies::new(509).allergies(); compare_allergy_vectors(expected, &allergies); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub struct Allergies(pub usize); #[derive(PartialEq, Debug)] pub enum Allergen { Eggs, Peanuts, Shellfish, Strawberries, Tomatoes, Chocolate, Pollen, Cats, } impl Allergies { pub fn new(score: usize) -> Allergies { Allergies(score) } pub fn is_allergic_to(&self, allergen: &Allergen) -> bool { let allergens = Allergies::allergens(); let index = allergens .iter() .position(|x: &Allergen| x == allergen) .unwrap(); (self.0 & (1 << index)) != 0 } pub fn allergies(&self) -> Vec<Allergen> { Allergies::allergens() .into_iter() .filter(|allergen| self.is_allergic_to(allergen)) .collect() } fn allergens() -> Vec<Allergen> { vec![ Allergen::Eggs, Allergen::Peanuts, Allergen::Shellfish, Allergen::Strawberries, Allergen::Tomatoes, Allergen::Chocolate, Allergen::Pollen, Allergen::Cats, ] } } #}
填充/相关
Variable Length Quantity
1. Readme
可变长度数量
实现可变长度数量编码和解码.
这项工作的目标是实现VLQ的编码/解码。
简而言之,此编码的目标是以节省字节的方式,对整数值进行编码。只有每个字节的前 7 位是有效的(右对齐;有点像 ASCII 字节)。因此,如果您有 32 位值,则必须解开为一系列 7 位字节。当然,根据您的整数,您将拥有可变数量的字节。要指出哪个是系列的最后一个字节,请将 #7 位清零。而所有前面的字节中,您都要设置#7 位。
所以,如果一个整数介于0-127
,它可以表示为一个字节。虽然 VLQ 可以处理任意大小的数字,但对于本练习,我们将仅限于适合 32 位无符号整数的数字。以下是整数作为 32 位值的示例,以及它们转换为的可变长度数量:
NUMBER VARIABLE QUANTITY
00000000 00
00000040 40
0000007F 7F
00000080 81 00
00002000 C0 00
00003FFF FF 7F
00004000 81 80 00
00100000 C0 80 00
001FFFFF FF FF 7F
00200000 81 80 80 00
08000000 C0 80 80 00
0FFFFFFF FF FF FF 7F
资源
一个糟糕的 Splice 开发人员,必须实现 MIDI 编码/解码.https://splice.com
2. 开始你的表演
#[derive(Debug, PartialEq)] pub enum Error { IncompleteNumber, Overflow, } /// Convert a list of numbers to a stream of bytes encoded with variable length encoding. pub fn to_bytes(values: &[u32]) -> Vec<u8> { unimplemented!("Convert the values {:?} to a list of bytes", values) } /// Given a stream of bytes, extract all numbers which are encoded in there. pub fn from_bytes(bytes: &[u8]) -> Result<Vec<u32>, Error> { unimplemented!("Convert the list of bytes {:?} to a list of numbers", bytes) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn to_single_byte() { assert_eq!(&[0x00], to_bytes(&[0x00]).as_slice()); assert_eq!(&[0x40], to_bytes(&[0x40]).as_slice()); assert_eq!(&[0x7f], to_bytes(&[0x7f]).as_slice()); } #[test] //#[ignore] fn to_double_byte() { assert_eq!(&[0x81, 0x00], to_bytes(&[0x80]).as_slice()); assert_eq!(&[0xc0, 0x00], to_bytes(&[0x2000]).as_slice()); assert_eq!(&[0xff, 0x7f], to_bytes(&[0x3fff]).as_slice()); } #[test] //#[ignore] fn to_triple_byte() { assert_eq!(&[0x81, 0x80, 0x00], to_bytes(&[0x4000]).as_slice()); assert_eq!(&[0xc0, 0x80, 0x00], to_bytes(&[0x10_0000]).as_slice()); assert_eq!(&[0xff, 0xff, 0x7f], to_bytes(&[0x1f_ffff]).as_slice()); } #[test] //#[ignore] fn to_quadruple_byte() { assert_eq!(&[0x81, 0x80, 0x80, 0x00], to_bytes(&[0x20_0000]).as_slice()); assert_eq!( &[0xc0, 0x80, 0x80, 0x00], to_bytes(&[0x0800_0000]).as_slice() ); assert_eq!( &[0xff, 0xff, 0xff, 0x7f], to_bytes(&[0x0fff_ffff]).as_slice() ); } #[test] //#[ignore] fn to_quintuple_byte() { assert_eq!( &[0x81, 0x80, 0x80, 0x80, 0x00], to_bytes(&[0x1000_0000]).as_slice() ); assert_eq!( &[0x8f, 0xf8, 0x80, 0x80, 0x00], to_bytes(&[0xff00_0000]).as_slice() ); assert_eq!( &[0x8f, 0xff, 0xff, 0xff, 0x7f], to_bytes(&[0xffff_ffff]).as_slice() ); } #[test] //#[ignore] fn from_bytes_test() { assert_eq!(Ok(vec![0x7f]), from_bytes(&[0x7f])); assert_eq!(Ok(vec![0x2000]), from_bytes(&[0xc0, 0x00])); assert_eq!(Ok(vec![0x1f_ffff]), from_bytes(&[0xff, 0xff, 0x7f])); assert_eq!(Ok(vec![0x20_0000]), from_bytes(&[0x81, 0x80, 0x80, 0x00])); assert_eq!( Ok(vec![0xffff_ffff]), from_bytes(&[0x8f, 0xff, 0xff, 0xff, 0x7f]) ); } #[test] //#[ignore] fn to_bytes_multiple_values() { assert_eq!(&[0x40, 0x7f], to_bytes(&[0x40, 0x7f]).as_slice()); assert_eq!( &[0x81, 0x80, 0x00, 0xc8, 0xe8, 0x56], to_bytes(&[0x4000, 0x12_3456]).as_slice() ); assert_eq!( &[ 0xc0, 0x00, 0xc8, 0xe8, 0x56, 0xff, 0xff, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0x81, 0x80, 0x00, ], to_bytes(&[0x2000, 0x12_3456, 0x0fff_ffff, 0x00, 0x3fff, 0x4000]).as_slice() ); } #[test] //#[ignore] fn from_bytes_multiple_values() { assert_eq!( Ok(vec![0x2000, 0x12_3456, 0x0fff_ffff, 0x00, 0x3fff, 0x4000]), from_bytes(&[ 0xc0, 0x00, 0xc8, 0xe8, 0x56, 0xff, 0xff, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0x81, 0x80, 0x00, ]) ); } #[test] //#[ignore] fn incomplete_byte_sequence() { assert_eq!(Err(Error::IncompleteNumber), from_bytes(&[0xff])); } #[test] //#[ignore] fn zero_incomplete_byte_sequence() { assert_eq!(Err(Error::IncompleteNumber), from_bytes(&[0x80])); } #[test] //#[ignore] fn overflow_u32() { assert_eq!( Err(Error::Overflow), from_bytes(&[0xff, 0xff, 0xff, 0xff, 0x7f]) ); } #[test] //#[ignore] fn chained_execution_is_identity() { let test = &[0xf2, 0xf6, 0x96, 0x9c, 0x3b, 0x39, 0x2e, 0x30, 0xb3, 0x24]; assert_eq!(Ok(Vec::from(&test[..])), from_bytes(&to_bytes(test))); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(Debug, PartialEq)] pub enum Error { IncompleteNumber, Overflow, } /// Convert a list of numbers to a stream of bytes encoded with variable length encoding. pub fn to_bytes(values: &[u32]) -> Vec<u8> { let mut res = vec![]; for value in values { res.append(&mut to_bytes_single(*value)); } res } fn to_bytes_single(mut value: u32) -> Vec<u8> { // over allocates, but avoids growth let mut res = Vec::with_capacity(4); // 0 must be handled specially, because we need to push one byte if value == 0 { return vec![0]; } while value > 0 { // take the lower 7 bits let mut tmp = (value & 0x7f) as u8; // remove them from the original value value >>= 7; // set continuation bit if !res.is_empty() { tmp |= 0x80; } res.push(tmp); } // order is wrong due to the way we pushed the data onto it res.reverse(); res } // Alternative solution with hardcoded borders // /// Convert a list of numbers to a stream of bytes encoded with variable length encoding. // pub fn to_bytes(values: &[u32]) -> Vec<u8> { // let mut res = vec![]; // // for &value in values { // if value <= 0x7f { // res.push(value as u8); // } else if value <= 0x3fff { // res.push(((value >> 7) & 0xff) as u8 | 0x80); // res.push((value & 0x7f) as u8); // } else if value <= 0x1f_ffff { // res.push(((value >> 14) & 0xff) as u8 | 0x80); // res.push(((value >> 7) & 0xff) as u8 | 0x80); // res.push((value & 0x7f) as u8); // } else if value <= 0x0fff_ffff { // res.push(((value >> 21) & 0xff) as u8 | 0x80); // res.push(((value >> 14) & 0xff) as u8 | 0x80); // res.push(((value >> 7) & 0xff) as u8 | 0x80); // res.push((value & 0x7f) as u8); // } else { // res.push(((value >> 28) & 0xff) as u8 | 0x80); // res.push(((value >> 21) & 0xff) as u8 | 0x80); // res.push(((value >> 14) & 0xff) as u8 | 0x80); // res.push(((value >> 7) & 0xff) as u8 | 0x80); // res.push((value & 0x7f) as u8); // } // } // res // } /// Given a stream of bytes, extract all numbers which are encoded in there. pub fn from_bytes(bytes: &[u8]) -> Result<Vec<u32>, Error> { let mut res = vec![]; let mut tmp = 0; for (i, b) in bytes.iter().enumerate() { // test if first 7 bit are set, to check for overflow if (tmp & 0xfe_00_00_00) > 0 { return Err(Error::Overflow); } // append bytes of b to tmp tmp = (tmp << 7) | (b & 0x7f) as u32; if 0x80 & b == 0 { // continuation bit not set, number if complete res.push(tmp); tmp = 0; } else { // check for incomplete bytes if i + 1 == bytes.len() { // the next index would be past the end, // i.e. there are no more bytes. return Err(Error::IncompleteNumber); } } } Ok(res) } #}
填充/相关
Phone Number
1. Readme
电话号码
整理用户输入的电话号码,以便他们可以发送短信.
这个**北美编号计划(NANP)**是北美洲、加拿大或百慕大群岛等许多国家使用的电话号码系统。所有 NANP 国家共享相同的国际国家代码:1
。
NANP 数字是十位数字,由三位编号——区域划分代码组成,俗称地区代码其次是一个七位数的本地号码。本地号码的前三位数字表示交换码,剩余是唯一的四位数字,这是用户号码。
格式通常表示为
(NXX)-NXX-XXXX
这里的N
是从 2 到 9 的任何数字,而X
是从 0 到 9 的任何数字.
您的任务是通过删除标点符号和国家代码(1),整理不同格式的电话号码。
例如,输入
+1 (613)-995-0253
613-995-0253
1 613 995 0253
613.995.0253
都应该产出
6139950253
**注:**因为这个练习只涉及 NANP 国家使用的电话号码,只有 1 被认为是有效的国家代码。
资源
JumpstartLab 的事件管理器http://tutorials.jumpstartlab.com/projects/eventmanager.html
2. 开始你的表演
pub fn number(user_number: &str) -> Option<String> { unimplemented!( "Given the number entered by user '{}', convert it into SMS-friendly format. If the entered number is not a valid NANP number, return None.", user_number ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn to_some_string(s: &str) -> Option<String> { Some(s.to_string()) } #[test] fn test_cleans_the_number() { assert_eq!(number("(223) 456-7890"), to_some_string("2234567890")); } #[test] //#[ignore] fn test_cleans_numbers_with_dots() { assert_eq!(number("223.456.7890"), to_some_string("2234567890")); } #[test] //#[ignore] fn test_cleans_numbers_with_multiple_spaces() { assert_eq!(number("223 456 7890 "), to_some_string("2234567890")); } #[test] //#[ignore] fn test_invalid_when_9_digits() { assert_eq!(number("123456789"), None); } #[test] //#[ignore] fn test_invalid_when_11_digits_does_not_start_with_a_1() { assert_eq!(number("22234567890"), None); } #[test] //#[ignore] fn test_valid_when_11_digits_and_starting_with_1() { assert_eq!(number("12234567890"), to_some_string("2234567890")); } #[test] //#[ignore] fn test_valid_when_11_digits_and_starting_with_1_even_with_punctuation() { assert_eq!(number("+1 (223) 456-7890"), to_some_string("2234567890")); } #[test] //#[ignore] fn test_invalid_when_more_than_11_digits() { assert_eq!(number("321234567890"), None); } #[test] //#[ignore] fn test_invalid_with_letters() { assert_eq!(number("123-abc-7890"), None); } #[test] //#[ignore] fn test_invalid_with_punctuations() { assert_eq!(number("123-@:!-7890"), None); } #[test] //#[ignore] fn test_invalid_if_area_code_does_not_start_with_2_9() { assert_eq!(number("(123) 456-7890"), None); } #[test] //#[ignore] fn test_invalid_if_exchange_code_does_not_start_with_2_9() { assert_eq!(number("(223) 056-7890"), None); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn number(s: &str) -> Option<String> { let digits: String = s.chars().filter(|&c| c.is_digit(10)).collect(); match digits.len() { 10 => match (digits.chars().nth(0), digits.chars().nth(3)) { (Some('0'), _) => None, (Some('1'), _) => None, (_, Some('0')) => None, (_, Some('1')) => None, _ => Some(digits), }, 11 => match digits.chars().nth(0) { Some('1') => Some(digits[1..].to_string()), _ => None, }, _ => None, } } #}
填充/相关
Wordy
1. Readme
罗唆
解析并运算简单的数学单词问题,将答案作为整数返回。
迭代 1 - 加法
将两个数字相加
What is 5 plus 13?
运算为 18.
处理大数和负数。
迭代 2 - 减法,乘法和除法
现在,执行其他三个操作.
What is 7 minus 5?
2
What is 6 multiplied by 4?
24
What is 25 divided by 5?
五
迭代 3 - 多个操作
按顺序处理一组操作.
由于这些是口头语言问题,从左到右运算表达式,忽略了典型的操作顺序(乘法优先级比加法高)。
What is 5 plus 13 plus 6?
24
What is 3 plus 2 multiplied by 3?
15(不是 9)
奖金 - 指数
如果你愿意,处理指数.
What is 2 raised to the 5th power?
32
资源
灵感来自 Extreme Startup 游戏中的一个生成的问题.https://github.com/rchatley/extreme_startup
2. 开始你的表演
pub struct WordProblem; pub fn answer(command: &str) -> Option<i32> { unimplemented!( "Return the result of the command '{}' or None, if the command is invalid.", command ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn addition() { let command = "What is 1 plus 1?"; assert_eq!(Some(2), answer(command)); } #[test] //#[ignore] fn more_addition() { let command = "What is 53 plus 2?"; assert_eq!(Some(55), answer(command)); } #[test] //#[ignore] fn addition_with_negative_numbers() { let command = "What is -1 plus -10?"; assert_eq!(Some(-11), answer(command)); } #[test] //#[ignore] fn large_addition() { let command = "What is 123 plus 45678?"; assert_eq!(Some(45801), answer(command)); } #[test] //#[ignore] fn subtraction() { let command = "What is 4 minus -12?"; assert_eq!(Some(16), answer(command)); } #[test] //#[ignore] fn multiplication() { let command = "What is -3 multiplied by 25?"; assert_eq!(Some(-75), answer(command)); } #[test] //#[ignore] fn division() { let command = "What is 33 divided by -3?"; assert_eq!(Some(-11), answer(command)); } #[test] //#[ignore] fn multiple_additions() { let command = "What is 1 plus 1 plus 1?"; assert_eq!(Some(3), answer(command)); } #[test] //#[ignore] fn addition_and_subtraction() { let command = "What is 1 plus 5 minus -2?"; assert_eq!(Some(8), answer(command)); } #[test] //#[ignore] fn multiple_subtraction() { let command = "What is 20 minus 4 minus 13?"; assert_eq!(Some(3), answer(command)); } #[test] //#[ignore] fn subtraction_then_addition() { let command = "What is 17 minus 6 plus 3?"; assert_eq!(Some(14), answer(command)); } #[test] //#[ignore] fn multiple_multiplications() { let command = "What is 2 multiplied by -2 multiplied by 3?"; assert_eq!(Some(-12), answer(command)); } #[test] //#[ignore] fn addition_and_multiplication() { let command = "What is -3 plus 7 multiplied by -2?"; assert_eq!(Some(-8), answer(command)); } #[test] //#[ignore] fn multiple_divisions() { let command = "What is -12 divided by 2 divided by -3?"; assert_eq!(Some(2), answer(command)); } #[test] //#[ignore] fn unknown_operation() { let command = "What is 52 cubed?"; assert!(answer(command).is_none()); } #[test] //#[ignore] fn non_math_question() { let command = "Who is the President of the United States?"; assert!(answer(command).is_none()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { struct Token<'a> { value: &'a str, } impl <'a> Token<'a> { fn is_valid(&self) -> bool { !self.value.is_empty() && (self.is_operand() || self.is_operator()) } fn is_operand(&self) -> bool { self.value.chars().all(|c| c.is_numeric() || c == '-') } fn is_operator(&self) -> bool { self.value == "plus" || self.value == "minus" || self.value == "multiplied" || self.value == "divided" } } pub fn answer(c: &str) -> Option<i32> { let mut t = tokens(c); let mut result: i32 = 0; let mut opr = "plus"; if t.len() <= 1 { None } else { while t.len() > 1 { result = evaluate(result, opr, operand(&t.remove(0))); opr = operator(&t.remove(0)); } result = evaluate(result, opr, operand(&t.remove(0))); Some(result) } } fn evaluate(r: i32, operator: &str, operand: i32) -> i32 { match operator { "plus" => r + operand, "minus" => r - operand, "multiplied" => r * operand, "divided" => r / operand, _ => r, } } fn operand(t: &Token) -> i32 { t.value.parse().unwrap() } fn operator<'a>(t: &Token<'a>) -> &'a str { t.value } fn tokens<'a>(command: &'a str) -> Vec<Token<'a>> { command .split(|c: char| c.is_whitespace() || c == '?') .map(|w| Token { value: w, }) .filter(|t| t.is_valid()) .collect() } #}
填充/相关
Tournament
1. Readme
锦标赛
统计一场小型足球比赛的结果.
基于一个输入文件,它包含哪个队与哪个队进行比赛,结果是什么。用下面的表格创建出一个文件:
Team | MP | W | D | L | P
Devastating Donkeys | 3 | 2 | 1 | 0 | 7
Allegoric Alaskans | 3 | 2 | 0 | 1 | 6
Blithering Badgers | 3 | 1 | 0 | 2 | 3
Courageous Californians | 3 | 0 | 1 | 2 | 1
那些缩写是什么意思?
- MP:赛次
- W:比赛赢了
- D:打平
- L:比赛输了
- P:分
一场胜利,3 分。打平 1 分。输了 0 分.
结果应该按分数下降排序。在平局的情况下,球队按字母顺序排列。
输入
你的梳理程序,将接收像这样的输入:
Allegoric Alaskans;Blithering Badgers;win
Devastating Donkeys;Courageous Californians;draw
Devastating Donkeys;Allegoric Alaskans;win
Courageous Californians;Blithering Badgers;loss
Blithering Badgers;Devastating Donkeys;loss
Allegoric Alaskans;Courageous Californians;win
一行中首个队伍名是主队。所以这一行
Allegoric Alaskans;Blithering Badgers;win
意味着,Allegoric Alaskans 打败了 Blithering Badgers。
这行:
Courageous Californians;Blithering Badgers;loss
意味着,Courageous Californians 输给了 Blithering Badgers
这行:
Devastating Donkeys;Courageous Californians;draw
意味着,Devastating Donkeys 与 Courageous Californians 打平
2. 开始你的表演
pub fn tally(match_results: &str) -> String { unimplemented!( "Given the result of the played matches '{}' return a properly formatted tally table string.", match_results ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn just_the_header_if_no_input() { let input = ""; let expected = "Team | MP | W | D | L | P"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn a_win_is_three_points_a_loss_is_zero_points() { let input = "Allegoric Alaskans;Blithering Badgers;win"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 1 | 1 | 0 | 0 | 3\n" + "Blithering Badgers | 1 | 0 | 0 | 1 | 0"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn a_win_can_also_be_expressed_as_a_loss() { let input = "Blithering Badgers;Allegoric Alaskans;loss"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 1 | 1 | 0 | 0 | 3\n" + "Blithering Badgers | 1 | 0 | 0 | 1 | 0"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn a_different_team_can_win() { let input = "Blithering Badgers;Allegoric Alaskans;win"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Blithering Badgers | 1 | 1 | 0 | 0 | 3\n" + "Allegoric Alaskans | 1 | 0 | 0 | 1 | 0"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn a_draw_is_one_point_each() { let input = "Allegoric Alaskans;Blithering Badgers;draw"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 1 | 0 | 1 | 0 | 1\n" + "Blithering Badgers | 1 | 0 | 1 | 0 | 1"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn there_can_be_more_than_one_match() { let input = "Allegoric Alaskans;Blithering Badgers;win\n".to_string() + "Allegoric Alaskans;Blithering Badgers;win"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 2 | 2 | 0 | 0 | 6\n" + "Blithering Badgers | 2 | 0 | 0 | 2 | 0"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn there_can_be_more_than_one_winner() { let input = "Allegoric Alaskans;Blithering Badgers;loss\n".to_string() + "Allegoric Alaskans;Blithering Badgers;win"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 2 | 1 | 0 | 1 | 3\n" + "Blithering Badgers | 2 | 1 | 0 | 1 | 3"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn there_can_be_more_than_two_teams() { let input = "Allegoric Alaskans;Blithering Badgers;win\n".to_string() + "Blithering Badgers;Courageous Californians;win\n" + "Courageous Californians;Allegoric Alaskans;loss"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 2 | 2 | 0 | 0 | 6\n" + "Blithering Badgers | 2 | 1 | 0 | 1 | 3\n" + "Courageous Californians | 2 | 0 | 0 | 2 | 0"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn typical_input() { let input = "Allegoric Alaskans;Blithering Badgers;win\n".to_string() + "Devastating Donkeys;Courageous Californians;draw\n" + "Devastating Donkeys;Allegoric Alaskans;win\n" + "Courageous Californians;Blithering Badgers;loss\n" + "Blithering Badgers;Devastating Donkeys;loss\n" + "Allegoric Alaskans;Courageous Californians;win"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Devastating Donkeys | 3 | 2 | 1 | 0 | 7\n" + "Allegoric Alaskans | 3 | 2 | 0 | 1 | 6\n" + "Blithering Badgers | 3 | 1 | 0 | 2 | 3\n" + "Courageous Californians | 3 | 0 | 1 | 2 | 1"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn incomplete_competition_not_all_pairs_have_played() { let input = "Allegoric Alaskans;Blithering Badgers;loss\n".to_string() + "Devastating Donkeys;Allegoric Alaskans;loss\n" + "Courageous Californians;Blithering Badgers;draw\n" + "Allegoric Alaskans;Courageous Californians;win"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 3 | 2 | 0 | 1 | 6\n" + "Blithering Badgers | 2 | 1 | 1 | 0 | 4\n" + "Courageous Californians | 2 | 0 | 1 | 1 | 1\n" + "Devastating Donkeys | 1 | 0 | 0 | 1 | 0"; assert_eq!(tally(&input), expected); } #[test] //#[ignore] fn ties_broken_alphabetically() { let input = "Courageous Californians;Devastating Donkeys;win\n".to_string() + "Allegoric Alaskans;Blithering Badgers;win\n" + "Devastating Donkeys;Allegoric Alaskans;loss\n" + "Courageous Californians;Blithering Badgers;win\n" + "Blithering Badgers;Devastating Donkeys;draw\n" + "Allegoric Alaskans;Courageous Californians;draw"; let expected = "Team | MP | W | D | L | P\n".to_string() + "Allegoric Alaskans | 3 | 2 | 1 | 0 | 7\n" + "Courageous Californians | 3 | 2 | 1 | 0 | 7\n" + "Blithering Badgers | 3 | 0 | 1 | 2 | 1\n" + "Devastating Donkeys | 3 | 0 | 1 | 2 | 1"; assert_eq!(tally(&input), expected); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::cmp::Ordering::Equal; use std::collections::HashMap; enum GameResult { Win, Draw, Loss, } struct TeamResult { wins: u32, draws: u32, losses: u32, } impl TeamResult { fn new() -> TeamResult { TeamResult { wins: 0, draws: 0, losses: 0, } } fn add_game_result(&mut self, result: GameResult) { match result { GameResult::Win => self.wins += 1, GameResult::Draw => self.draws += 1, GameResult::Loss => self.losses += 1, } } } pub fn tally(input: &str) -> String { let mut results: HashMap<String, TeamResult> = HashMap::new(); for line in input.to_string().lines() { let parts: Vec<&str> = line.trim_right().split(';').collect(); if parts.len() != 3 { continue; } let team1 = parts[0]; let team2 = parts[1]; let outcome = parts[2]; match outcome { "win" => { add_game_result(&mut results, team1.to_string(), GameResult::Win); add_game_result(&mut results, team2.to_string(), GameResult::Loss); } "draw" => { add_game_result(&mut results, team1.to_string(), GameResult::Draw); add_game_result(&mut results, team2.to_string(), GameResult::Draw); } "loss" => { add_game_result(&mut results, team1.to_string(), GameResult::Loss); add_game_result(&mut results, team2.to_string(), GameResult::Win); } _ => (), // Ignore bad lines } } write_tally(&results) } fn write_tally(results: &HashMap<String, TeamResult>) -> String { let mut v: Vec<_> = results .iter() .map(|(team, r)| { let games = r.wins + r.draws + r.losses; let points = r.wins * 3 + r.draws; (team, games, r, points) }) .collect(); // Sort by points descending, then name A-Z. v.sort_by(|a, b| match a.3.cmp(&(b.3)).reverse() { Equal => a.0.cmp(&(b.0)), other => other, }); let mut lines = vec![format!("{:30} | MP | W | D | L | P", "Team")]; lines.extend(v.iter().map(|&(ref team, games, r, points)| { format!( "{:30} | {:2} | {:2} | {:2} | {:2} | {:2}", team, games, r.wins, r.draws, r.losses, points ) })); lines.join("\n") } fn add_game_result(results: &mut HashMap<String, TeamResult>, team: String, result: GameResult) { results .entry(team) .or_insert(TeamResult::new()) .add_game_result(result); } #}
填充/相关
Custom Set
1. Readme
自定义集合
创建自定义 set 类型。
有时需要定义某种类型的自定义数据结构,例如一个集合(set)。在本练习中,您将定义自己的集合。它的内部工作原理无关紧要,只要它的行为就像一组唯一元素的集合。
2. 开始你的表演
use std::fmt::Debug; use std::marker::PhantomData; #[derive(Debug, PartialEq)] pub struct CustomSet<T> { // This field is here to make the template compile and not to // complain about unused type parameter 'T'. Once you start // solving the exercise, delete this field and the 'std::marker::PhantomData' // import. phantom: PhantomData<T>, } impl<T: Debug> CustomSet<T> { pub fn new(input: &[T]) -> Self { unimplemented!( "From the given input '{:?}' construct a new CustomSet struct.", input ); } pub fn contains(&self, element: &T) -> bool { unimplemented!( "Determine if the '{:?}' element is present in the CustomSet struct.", element ); } pub fn add(&mut self, element: T) { unimplemented!("Add the '{:?}' element to the CustomSet struct.", element); } pub fn is_subset(&self, other: &Self) -> bool { unimplemented!( "Determine if the CustomSet struct is a subset of the other '{:?}' struct.", other ); } pub fn is_empty(&self) -> bool { unimplemented!("Determine if the CustomSet struct is empty."); } pub fn is_disjoint(&self, other: &Self) -> bool { unimplemented!( "Determine if the CustomSet struct and the other struct '{:?}' are disjoint.", other ); } pub fn intersection(&self, other: &Self) -> Self { unimplemented!("Construct a new CustomSet struct that is an intersection between current struct and the other struct '{:?}'.", other); } pub fn difference(&self, other: &Self) -> Self { unimplemented!("Construct a new CustomSet struct that is a difference between current struct and the other struct '{:?}'.", other); } pub fn union(&self, other: &Self) -> Self { unimplemented!("Construct a new CustomSet struct that is an union between current struct and the other struct '{:?}'.", other); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn sets_with_no_elements_are_empty() { let set: CustomSet<()> = CustomSet::new(&[]); assert!(set.is_empty()); } #[test] //#[ignore] fn sets_with_elements_are_not_empty() { let set = CustomSet::new(&[1]); assert!(!set.is_empty()); } #[test] //#[ignore] fn nothing_is_contained_in_an_empty_set() { let set = CustomSet::new(&[]); assert!(!set.contains(&1)); } #[test] //#[ignore] fn true_when_the_element_is_in_the_set() { let set = CustomSet::new(&[1, 2, 3]); assert!(set.contains(&1)); } #[test] //#[ignore] fn false_when_the_element_is_not_in_the_set() { let set = CustomSet::new(&[1, 2, 3]); assert!(!set.contains(&4)); } #[test] //#[ignore] fn empty_sets_are_subsets_of_each_other() { let set1: CustomSet<()> = CustomSet::new(&[]); let set2: CustomSet<()> = CustomSet::new(&[]); assert!(set1.is_subset(&set2)); assert!(set2.is_subset(&set1)); } #[test] //#[ignore] fn empty_set_is_subset_of_non_empty_set() { let set1 = CustomSet::new(&[]); let set2 = CustomSet::new(&[1]); assert!(set1.is_subset(&set2)); } #[test] //#[ignore] fn non_empty_set_is_not_subset_of_empty_set() { let set1 = CustomSet::new(&[1]); let set2 = CustomSet::new(&[]); assert!(!set1.is_subset(&set2)); } #[test] //#[ignore] fn sets_with_same_elements_are_subsets() { let set1 = CustomSet::new(&[1, 2, 3]); let set2 = CustomSet::new(&[1, 2, 3]); assert!(set1.is_subset(&set2)); assert!(set2.is_subset(&set1)); } #[test] //#[ignore] fn set_contained_in_other_set_is_a_subset() { let set1 = CustomSet::new(&[1, 2, 3]); let set2 = CustomSet::new(&[4, 1, 2, 3]); assert!(set1.is_subset(&set2)); } #[test] //#[ignore] fn set_not_contained_in_other_set_is_not_a_subset_one() { let set1 = CustomSet::new(&[1, 2, 3]); let set2 = CustomSet::new(&[4, 1, 3]); assert!(!set1.is_subset(&set2)); } #[test] //#[ignore] fn empty_sets_are_disjoint_with_each_other() { let set1: CustomSet<()> = CustomSet::new(&[]); let set2: CustomSet<()> = CustomSet::new(&[]); assert!(set1.is_disjoint(&set2)); assert!(set2.is_disjoint(&set1)); } #[test] //#[ignore] fn empty_set_disjoint_with_non_empty_set() { let set1 = CustomSet::new(&[]); let set2 = CustomSet::new(&[1]); assert!(set1.is_disjoint(&set2)); } #[test] //#[ignore] fn non_empty_set_disjoint_with_empty_set() { let set1 = CustomSet::new(&[1]); let set2 = CustomSet::new(&[]); assert!(set1.is_disjoint(&set2)); } #[test] //#[ignore] fn sets_with_one_element_in_common_are_not_disjoint() { let set1 = CustomSet::new(&[1, 2]); let set2 = CustomSet::new(&[2, 3]); assert!(!set1.is_disjoint(&set2)); assert!(!set2.is_disjoint(&set1)); } #[test] //#[ignore] fn sets_with_no_elements_in_common_are_disjoint() { let set1 = CustomSet::new(&[1, 2]); let set2 = CustomSet::new(&[3, 4]); assert!(set1.is_disjoint(&set2)); assert!(set2.is_disjoint(&set1)); } #[test] //#[ignore] fn empty_sets_are_equal() { let set1: CustomSet<()> = CustomSet::new(&[]); let set2: CustomSet<()> = CustomSet::new(&[]); assert_eq!(set1, set2); } #[test] //#[ignore] fn empty_set_is_not_equal_to_a_non_empty_set() { let set1 = CustomSet::new(&[]); let set2 = CustomSet::new(&[1, 2, 3]); assert_ne!(set1, set2); } #[test] //#[ignore] fn non_empty_set_is_not_equal_to_an_empty_set() { let set1 = CustomSet::new(&[1, 2, 3]); let set2 = CustomSet::new(&[]); assert_ne!(set1, set2); } #[test] //#[ignore] fn sets_with_the_same_elements_are_equal() { let set1 = CustomSet::new(&[1, 2]); let set2 = CustomSet::new(&[2, 1]); assert_eq!(set1, set2); } #[test] //#[ignore] fn sets_with_different_elements_are_not_equal() { let set1 = CustomSet::new(&[1, 2, 3]); let set2 = CustomSet::new(&[2, 1, 4]); assert_ne!(set1, set2); } #[test] //#[ignore] fn add_to_empty_set() { let mut set = CustomSet::new(&[]); set.add(3); assert_eq!(set, CustomSet::new(&[3])); } #[test] //#[ignore] fn add_to_non_empty_set() { let mut set = CustomSet::new(&[1, 2, 4]); set.add(3); assert_eq!(set, CustomSet::new(&[1, 2, 3, 4])); } #[test] //#[ignore] fn add_existing_element() { let mut set = CustomSet::new(&[1, 2, 3]); set.add(3); assert_eq!(set, CustomSet::new(&[1, 2, 3])); } #[test] //#[ignore] fn intersecting_empty_sets_return_empty_set() { let set1: CustomSet<()> = CustomSet::new(&[]); let set2: CustomSet<()> = CustomSet::new(&[]); assert_eq!(set1.intersection(&set2), CustomSet::new(&[])); } #[test] //#[ignore] fn intersecting_empty_set_with_non_empty_returns_empty_set() { let set1 = CustomSet::new(&[]); let set2 = CustomSet::new(&[3, 2, 5]); assert_eq!(set1.intersection(&set2), CustomSet::new(&[])); } #[test] //#[ignore] fn intersecting_non_empty_set_with_empty_returns_empty_set() { let set1 = CustomSet::new(&[1, 2, 3, 4]); let set2 = CustomSet::new(&[]); assert_eq!(set1.intersection(&set2), CustomSet::new(&[])); } #[test] //#[ignore] fn intersection_of_two_sets_with_no_shared_elements_is_an_empty_set() { let set1 = CustomSet::new(&[1, 2, 3]); let set2 = CustomSet::new(&[4, 5, 6]); assert_eq!(set1.intersection(&set2), CustomSet::new(&[])); assert_eq!(set2.intersection(&set1), CustomSet::new(&[])); } #[test] //#[ignore] fn intersection_of_two_sets_with_shared_elements_is_a_set_of_the_shared_elements() { let set1 = CustomSet::new(&[1, 2, 3, 4]); let set2 = CustomSet::new(&[3, 2, 5]); assert_eq!(set1.intersection(&set2), CustomSet::new(&[2, 3])); assert_eq!(set2.intersection(&set1), CustomSet::new(&[2, 3])); } #[test] //#[ignore] fn difference_of_two_empty_sets_is_empty_set() { let set1: CustomSet<()> = CustomSet::new(&[]); let set2: CustomSet<()> = CustomSet::new(&[]); assert_eq!(set1.difference(&set2), CustomSet::new(&[])); } #[test] //#[ignore] fn difference_of_an_empty_and_non_empty_set_is_an_empty_set() { let set1 = CustomSet::new(&[]); let set2 = CustomSet::new(&[3, 2, 5]); assert_eq!(set1.difference(&set2), CustomSet::new(&[])); } #[test] //#[ignore] fn difference_of_a_non_empty_set_and_empty_set_is_the_non_empty_set() { let set1 = CustomSet::new(&[1, 2, 3, 4]); let set2 = CustomSet::new(&[]); assert_eq!(set1.difference(&set2), CustomSet::new(&[1, 2, 3, 4])); } #[test] //#[ignore] fn difference_of_two_non_empty_sets_is_elements_only_in_first_set_one() { let set1 = CustomSet::new(&[3, 2, 1]); let set2 = CustomSet::new(&[2, 4]); assert_eq!(set1.difference(&set2), CustomSet::new(&[1, 3])); } #[test] //#[ignore] fn union_of_two_empty_sets_is_empty_set() { let set1: CustomSet<()> = CustomSet::new(&[]); let set2: CustomSet<()> = CustomSet::new(&[]); assert_eq!(set1.union(&set2), CustomSet::new(&[])); } #[test] //#[ignore] fn union_of_empty_set_and_non_empty_set_is_all_elements() { let set1 = CustomSet::new(&[]); let set2 = CustomSet::new(&[2]); assert_eq!(set1.union(&set2), CustomSet::new(&[2])); } #[test] //#[ignore] fn union_of_non_empty_set_and_empty_set_is_the_non_empty_set() { let set1 = CustomSet::new(&[1, 3]); let set2 = CustomSet::new(&[]); assert_eq!(set1.union(&set2), CustomSet::new(&[1, 3])); } #[test] //#[ignore] fn union_of_non_empty_sets_contains_all_unique_elements() { let set1 = CustomSet::new(&[1, 3]); let set2 = CustomSet::new(&[2, 3]); assert_eq!(set1.union(&set2), CustomSet::new(&[3, 2, 1])); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(Debug)] pub struct CustomSet<T> { collection: Vec<T>, } impl<T: Ord + Clone> PartialEq for CustomSet<T> { fn eq(&self, other: &Self) -> bool { self.collection.iter().all(|x| other.contains(&x)) && other.collection.iter().all(|x| self.contains(&x)) } } impl<T: Ord + Clone> CustomSet<T> { pub fn new(inputs: &[T]) -> CustomSet<T> { let mut s = CustomSet { collection: Vec::new(), }; for input in inputs { s.add(input.clone()); } s } pub fn add(&mut self, element: T) { if !self.contains(&element) { self.collection.push(element) } } pub fn contains(&self, other: &T) -> bool { self.collection.contains(other) } pub fn is_empty(&self) -> bool { self.collection.is_empty() } pub fn is_subset(&self, other: &Self) -> bool { self.collection.iter().all(|x| other.contains(x)) } pub fn is_disjoint(&self, other: &Self) -> bool { !self.collection.iter().any(|x| other.contains(x)) } pub fn intersection(&self, other: &Self) -> CustomSet<T> { CustomSet::new(&self.collection .iter() .cloned() .filter(|c| other.contains(c)) .collect::<Vec<_>>()) } pub fn union(&self, other: &Self) -> CustomSet<T> { CustomSet::new(&self.collection .iter() .cloned() .chain(other.collection.iter().cloned()) .collect::<Vec<_>>()) } pub fn difference(&self, other: &Self) -> CustomSet<T> { CustomSet::new(&self.collection .iter() .cloned() .filter(|c| !other.contains(c)) .collect::<Vec<_>>()) } } #}
填充/相关
Alphametics
1. Readme
算术谜题
写一个函数来解决字母谜题.
Alphametics是一个拼图,其中单词中的字母被数字替换.
例如SEND + MORE = MONEY
:
S E N D
M O R E +
-----------
M O N E Y
用有效数字替换它们会给出:
9 5 6 7
1 0 8 5 +
-----------
1 0 6 5 2
这是正确的,因为每个字母都被不同的数字替换,之后单词会被翻译成数字,然后产生有效的总和。
每个字母必须代表不同的数字,并且多位数的首数字不能是 0 。
写一个函数来解决字母谜题.
2. 开始你的表演
use std::collections::HashMap; pub fn solve(input: &str) -> Option<HashMap<char, u8>> { unimplemented!("Solve the alphametic {:?}", input) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // use std::collections::HashMap; fn assert_alphametic_solution_eq(puzzle: &str, solution: &[(char, u8)]) { let answer = solve(puzzle); let solution: HashMap<char, u8> = solution.iter().cloned().collect(); assert_eq!(answer, Some(solution)); } #[test] fn test_with_three_letters() { assert_alphametic_solution_eq("I + BB == ILL", &[('I', 1), ('B', 9), ('L', 0)]); } #[test] //#[ignore] fn test_must_have_unique_value_for_each_letter() { let answer = solve("A == B"); assert_eq!(answer, None); } #[test] //#[ignore] fn test_leading_zero_solution_is_invalid() { let answer = solve("ACA + DD == BD"); assert_eq!(answer, None); } #[test] //#[ignore] fn test_puzzle_with_four_letters() { assert_alphametic_solution_eq("AS + A == MOM", &[('A', 9), ('S', 2), ('M', 1), ('O', 0)]); } #[test] //#[ignore] fn test_puzzle_with_six_letters() { assert_alphametic_solution_eq( "NO + NO + TOO == LATE", &[('N', 7), ('O', 4), ('T', 9), ('L', 1), ('A', 0), ('E', 2)], ); } #[test] //#[ignore] fn test_puzzle_with_seven_letters() { assert_alphametic_solution_eq( "HE + SEES + THE == LIGHT", &[ ('E', 4), ('G', 2), ('H', 5), ('I', 0), ('L', 1), ('S', 9), ('T', 7), ], ); } #[test] //#[ignore] fn test_puzzle_with_eight_letters() { assert_alphametic_solution_eq( "SEND + MORE == MONEY", &[ ('S', 9), ('E', 5), ('N', 6), ('D', 7), ('M', 1), ('O', 0), ('R', 8), ('Y', 2), ], ); } #[test] //#[ignore] fn test_puzzle_with_ten_letters() { assert_alphametic_solution_eq( "AND + A + STRONG + OFFENSE + AS + A + GOOD == DEFENSE", &[ ('A', 5), ('D', 3), ('E', 4), ('F', 7), ('G', 8), ('N', 0), ('O', 2), ('R', 1), ('S', 6), ('T', 9), ], ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { // This is a brute-force solution, use `cargo test --release` for faster testing extern crate itertools; extern crate permutohedron; use itertools::Itertools; use permutohedron::Heap as Permutations; use std::char; use std::collections::HashMap; use std::collections::HashSet; fn test_equation(puzzle: &str, substitutions: &HashMap<char, u8>) -> bool { // Create a new String with characters changed to numbers let puzzle: String = puzzle .chars() .map(|c| { if let Some(&n) = substitutions.get(&c) { // If the character is in the substitutions, get the number and // convert it to a char char::from_digit(n as u32, 10).unwrap() } else { // Otherwise just copy over the character c } }) .collect(); // Split the puzzle into left and right side let equation: Vec<&str> = puzzle.split("==").collect(); // Parse the number on the right side let right = equation[1].trim().parse::<u32>().unwrap(); // Sum the parts on the left side let left: u32 = equation[0] .split('+') .map(str::trim) .map(|n| n.parse::<u32>().unwrap()) .sum(); // Create a String with just the numbers and spaces let just_numbers = puzzle .chars() .filter(|c| c.is_digit(10) || c.is_whitespace()) .collect::<String>(); // Split this into the numbers and check every number's first character let no_leading_zeroes = just_numbers .split_whitespace() .all(|number| number.chars().next().unwrap() != '0'); // Return true if left and right side is equal and the equation doesnt // contain leading zeroes. left == right && no_leading_zeroes } pub fn solve(puzzle: &str) -> Option<HashMap<char, u8>> { // Get unique letters from the puzzle let letters: HashSet<char> = puzzle .chars() .filter(|&c| c.is_alphabetic() && c.is_uppercase()) .collect(); let letters: Vec<char> = letters.into_iter().collect(); // All available numbers for substitution let numbers: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; // Iterate every combination with the length of unique letters in the puzzle for combinations in numbers.iter().combinations(letters.len()) { let mut c = combinations; let permutations = Permutations::new(&mut c); // Iterate every permutation of a letter combination for p in permutations { let substitution: HashMap<char, u8> = letters.iter().zip(p).map(|(&c, &n)| (c, n)).collect(); if test_equation(puzzle, &substitution) { // We found a good substitution return Some(substitution); } } } // If we tested every combination and did not found a solution then return None None } #}
填充/相关
Two Bucket
1. Readme
两个桶
给定两个不同尺寸的桶,演示如何通过两个桶,策略性地传输液体来测量精确的升数.
由于这个数学问题,很容易受到解释/个体方法的影响,因此这些测试代码专门针对期望一个总体解决方案而编写.
为了提供帮助,测试代码首先为桶(1/2)装满。这意味着,若开始时,选择较大的桶装满,那就不会出现,较小的桶装满而较大的桶为空的情况。(也就是说,起点应该用较小桶);不然这会破坏比较两种方法的目的!
您的程序将作为输入:
- 桶 1 的大小
- 桶 2 的大小
- 要达到的理想升数
- 首先要装满哪个桶,要么是桶 1 还是桶 2
您的计划应确定:
- 达到所需的升数,所需的”移动”总数,包括第一次装满
- 哪个桶应该以所需的升数结束(假设这是桶 A) - 要么桶 1 ,要么桶 2
- 另一个桶中,剩下多少升(桶 B)
注意:任何时候对其中一个或两个桶进行更改,都计为一(1)次移动。
示例:桶 1 最多可容纳 7 升,桶 2 最多可容纳 11 升。让我们说桶 1,第一步,持有 7 升,桶 2 持有 8 升(7,8)。如果您清空桶 1,并且不对桶 2 进行任何更改,则分别为 0 升和 8 升(0,8),这将作为一个”移动”。相反,如果你已经把桶 1 倒入桶 2,直到桶 2 充满,那留下的,桶 1 中的 4 升和桶 2 中的 11 升(4,11),这也算作只有一个”移动”。
总而言之,唯一有效的行动是:
- 从一个桶,倒到另一个桶
- 清空一个桶,对另一个什么都不做
- 装满一个桶,对另一个什么也不做
写<3 于全栈教程 Academy作 者:Lindsay Levine.
资源
水浇注问题http://demonstrations.wolfram.com/WaterPouringProblem/
2. 开始你的表演
#[derive(PartialEq, Eq, Debug)] pub enum Bucket { One, Two, } /// A struct to hold your results in. #[derive(PartialEq, Eq, Debug)] pub struct BucketStats { /// The total number of "moves" it should take to reach the desired number of liters, including /// the first fill. pub moves: u8, /// Which bucket should end up with the desired number of liters? (Either "one" or "two") pub goal_bucket: Bucket, /// How many liters are left in the other bucket? pub other_bucket: u8, } /// Solve the bucket problem pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> BucketStats { unimplemented!( "Given one bucket of capacity {}, another of capacity {}, starting with {:?}, find pours to reach {}", capacity_1, capacity_2, start_bucket, goal, ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_case_1() { assert_eq!( solve(3, 5, 1, &Bucket::One), BucketStats { moves: 4, goal_bucket: Bucket::One, other_bucket: 5, } ); } #[test] //#[ignore] fn test_case_2() { assert_eq!( solve(3, 5, 1, &Bucket::Two), BucketStats { moves: 8, goal_bucket: Bucket::Two, other_bucket: 3, } ); } #[test] //#[ignore] fn test_case_3() { assert_eq!( solve(7, 11, 2, &Bucket::One), BucketStats { moves: 14, goal_bucket: Bucket::One, other_bucket: 11, } ); } #[test] //#[ignore] fn test_case_4() { assert_eq!( solve(7, 11, 2, &Bucket::Two), BucketStats { moves: 18, goal_bucket: Bucket::Two, other_bucket: 7, } ); } #[test] //#[ignore] fn test_case_5() { assert_eq!( solve(1, 3, 3, &Bucket::Two), BucketStats { moves: 1, goal_bucket: Bucket::Two, other_bucket: 0, } ); } #[test] //#[ignore] fn test_case_6() { assert_eq!( solve(2, 3, 3, &Bucket::One), BucketStats { moves: 2, goal_bucket: Bucket::Two, other_bucket: 2, } ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::{HashSet, VecDeque}; // We can turn this problem into a simple graph searching problem. Each move represents an // edge in our graph; the buckets' fill states form the graph's vertices. For example, say bucket // one holds up to 7 liters, and bucket two holds up to 11 liters, and at the current step, bucket // one has 7 liters and bucket two has 8 liters: (7, 8). By emptying the first bucket, we form an // edge (7, 8) -> (0, 8). Similarly, by pouring the first bucket into the second, we form an edge // (7, 8) -> (4, 11). // // Since we want to minimize the number of moves, we search the graph breadth-first, starting with // the configuration provided as the problem input. Note that, to avoid cheating, we mark both // possible starting configurations as visited; otherwise, our search might just empty the initial // bucket and fill the other one. #[derive(PartialEq, Eq, Debug)] pub enum Bucket { One, Two, } /// A struct to hold your results in. #[derive(PartialEq, Eq, Debug)] pub struct BucketStats { /// The total number of "moves" it should take to reach the desired number of liters, including /// the first fill. pub moves: u8, /// Which bucket should end up with the desired number of liters? (Either "one" or "two") pub goal_bucket: Bucket, /// How many liters are left in the other bucket? pub other_bucket: u8, } /// Solve the bucket problem pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> BucketStats { let state = match *start_bucket { Bucket::One => (capacity_1, 0), Bucket::Two => (0, capacity_2), }; let mut next_search = VecDeque::new(); let mut visited = HashSet::new(); let mut moves = 1; next_search.push_front(state); // "Visit" both starting states. This will ensure that we don't cheat, i.e. // empty our starting bucket completely and fill the other bucket. visited.insert((capacity_1, 0)); visited.insert((0, capacity_2)); loop { let mut current_search = next_search; next_search = VecDeque::new(); for state in current_search.drain(0..) { let (bucket_1, bucket_2) = state; if bucket_1 == goal { return BucketStats { moves: moves, goal_bucket: Bucket::One, other_bucket: bucket_2, }; } else if bucket_2 == goal { return BucketStats { moves: moves, goal_bucket: Bucket::Two, other_bucket: bucket_1, }; } // Empty the first bucket let empty_1 = (0, bucket_2); if !visited.contains(&empty_1) { next_search.push_front(empty_1); visited.insert(empty_1); } // Empty the second bucket let empty_2 = (bucket_1, 0); if !visited.contains(&empty_2) { next_search.push_front(empty_2); visited.insert(empty_2); } // Fill the first bucket let fill_1 = (capacity_1, bucket_2); if !visited.contains(&fill_1) { next_search.push_front(fill_1); visited.insert(fill_1); } // Fill the second bucket let fill_2 = (bucket_1, capacity_2); if !visited.contains(&fill_2) { next_search.push_front(fill_2); visited.insert(fill_2); } // Pour the first bucket into the second bucket let pour_1_into_2 = if bucket_1 + bucket_2 <= capacity_1 { (bucket_1 + bucket_2, 0) } else { (capacity_1, bucket_1 + bucket_2 - capacity_1) }; if !visited.contains(&pour_1_into_2) { next_search.push_front(pour_1_into_2); visited.insert(pour_1_into_2); } // Pour the second bucket into the first bucket let pour_2_into_1 = if bucket_1 + bucket_2 <= capacity_2 { (0, bucket_1 + bucket_2) } else { (bucket_1 + bucket_2 - capacity_2, capacity_2) }; if !visited.contains(&pour_2_into_1) { next_search.push_front(pour_2_into_1); visited.insert(pour_2_into_1); } } moves += 1; } } #}
填充/相关
Pig Latin
1. Readme
猪的拉丁文
实现一个从英语翻译成猪拉丁语的程序.
猪拉丁语是一种拼凑的儿童语言,目的是使人困惑。它遵循一些简单的规则(下面),但是当它说得很快时,对于非儿童(以及非母语者)来说真的很难理解.
- 规则 1如果一个单词以元音开头,在单词的末尾加上一个”ay”音。请注意,在单词开头的”xr”和”yt”会产生元音(例如 “xray” -> “xrayay”, “yttria” -> “yttriaay”)。
- 规则 2如果一个单词以辅音开头,把它移到单词的末尾,然后在单词的末尾加上一个”ay”音。辅音可以由多个辅音组成,例如辅音群(例如”chair” -> “airchay”).
- 规则 3如果一个单词以辅音开头,后面跟着”qu”,把它移动到单词的结尾,然后在单词的结尾加上”ay”音(例如,”square” -> “aresquay”).
- 规则 4如果一个单词在辅音群后面包含”y”,或者作为两个字母元音的单词的第二个字母(例如,”rhythm” -> “ythmrhay”, “my” -> “ymay”)。
边缘案例还有一些规则,也有区域性的变化.
见http://en.wikipedia.org/wiki/Pig_latin更多细节.
资源
猪拉丁文运动,在第一次超声波教学中的应用https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/
2. 开始你的表演
pub fn translate(input: &str) -> String { unimplemented!( "Using the Pig Latin text transformation rules, convert the given input '{}'", input ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_word_beginning_with_a() { assert_eq!(translate("apple"), "appleay"); } #[test] //#[ignore] fn test_word_beginning_with_e() { assert_eq!(translate("ear"), "earay"); } #[test] //#[ignore] fn test_word_beginning_with_i() { assert_eq!(translate("igloo"), "iglooay"); } #[test] //#[ignore] fn test_word_beginning_with_o() { assert_eq!(translate("object"), "objectay"); } #[test] //#[ignore] fn test_word_beginning_with_u() { assert_eq!(translate("under"), "underay"); } #[test] //#[ignore] fn test_word_beginning_with_a_vowel_and_followed_by_a_qu() { assert_eq!(translate("equal"), "equalay"); } #[test] //#[ignore] fn test_word_beginning_with_p() { assert_eq!(translate("pig"), "igpay"); } #[test] //#[ignore] fn test_word_beginning_with_k() { assert_eq!(translate("koala"), "oalakay"); } #[test] //#[ignore] fn test_word_beginning_with_y() { assert_eq!(translate("yellow"), "ellowyay"); } #[test] //#[ignore] fn test_word_beginning_with_x() { assert_eq!(translate("xenon"), "enonxay"); } #[test] //#[ignore] fn test_word_beginning_with_q_without_a_following_u() { assert_eq!(translate("qat"), "atqay"); } #[test] //#[ignore] fn test_word_beginning_with_ch() { assert_eq!(translate("chair"), "airchay"); } #[test] //#[ignore] fn test_word_beginning_with_qu() { assert_eq!(translate("queen"), "eenquay"); } #[test] //#[ignore] fn test_word_beginning_with_qu_and_a_preceding_consonant() { assert_eq!(translate("square"), "aresquay"); } #[test] //#[ignore] fn test_word_beginning_with_th() { assert_eq!(translate("therapy"), "erapythay"); } #[test] //#[ignore] fn test_word_beginning_with_thr() { assert_eq!(translate("thrush"), "ushthray"); } #[test] //#[ignore] fn test_word_beginning_with_sch() { assert_eq!(translate("school"), "oolschay"); } #[test] //#[ignore] fn test_word_beginning_with_yt() { assert_eq!(translate("yttria"), "yttriaay"); } #[test] //#[ignore] fn test_word_beginning_with_xr() { assert_eq!(translate("xray"), "xrayay"); } #[test] //#[ignore] fn test_y_is_treated_like_a_vowel_at_the_end_of_a_consonant_cluster() { assert_eq!(translate("rhythm"), "ythmrhay"); } #[test] //#[ignore] fn test_a_whole_phrase() { assert_eq!(translate("quick fast run"), "ickquay astfay unray"); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[macro_use] extern crate lazy_static; extern crate regex; use regex::Regex; // Regular expressions from Python version of exercism pub fn translate_word(word: &str) -> String { // Prevent creation and compilation at every call. // These are compiled exactly once lazy_static! { // Detects if it starts with a vowel static ref VOWEL: Regex = Regex::new(r"^([aeiou]|y[^aeiou]|xr)[a-z]*").unwrap(); // Detects splits for initial consonants static ref CONSONANTS: Regex = Regex::new(r"^([^aeiou]?qu|[^aeiou][^aeiouy]*)([a-z]*)").unwrap(); } if VOWEL.is_match(word) { String::from(word) + "ay" } else { let caps = CONSONANTS.captures(word).unwrap(); String::from(&caps[2]) + &caps[1] + "ay" } } pub fn translate(text: &str) -> String { text.split(" ") .map(|w| translate_word(w)) .collect::<Vec<_>>() .join(" ") } #}
填充/相关
Diamond
1. Readme
钻石
钻石 kata 将一个字母作为输入,并以菱形输出。给定一个字母,它会打印一个以’A’开头的钻石,并在最宽处提供所提供的字母。
要求
- 第一行包含一个’A’.
- 最后一行包含一个’A’.
- 除第一行和最后一行之外的所有行都有两个完全相同的字母.
- 所有行都具有与前导空格一样多的尾随空格.(这可能是 0).
- 钻石是水平对称的.
- 钻石是垂直对称的.
- 钻石具有方形(宽度等于高度).
- 字母形成菱形.
- 上半部分的字母按升序排列.
- 下半部分的字母按降序排列.
- 四个角(包含空格)是三角形.
例子
在以下示例中,空格表示为·
字符.
字母’A’的钻石:
A
字母’C’的钻石:
··A··
·B·B·
C···C
·B·B·
··A··
字母’E’的钻石:
····A····
···B·B···
··C···C··
·D·····D·
E·······E
·D·····D·
··C···C··
···B·B···
····A····
资源
Seb Rosehttp://claysnow.co.uk/recycling-tests-in-tdd/
2. 开始你的表演
pub fn get_diamond(c: char) -> Vec<String> { unimplemented!( "Return the vector of strings which represent the diamond with particular char {}", c ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_a() { assert_eq!(get_diamond('A'), vec!["A"]); } #[test] //#[ignore] fn test_b() { assert_eq!(get_diamond('B'), vec![" A ", "B B", " A "]); } #[test] //#[ignore] fn test_c() { assert_eq!( get_diamond('C'), vec![" A ", " B B ", "C C", " B B ", " A "] ); } #[test] //#[ignore] fn test_d() { assert_eq!( get_diamond('D'), vec![" A ", " B B ", " C C ", "D D", " C C ", " B B ", " A ",] ); } #[test] //#[ignore] fn test_e() { assert_eq!( get_diamond('Z'), vec![ " A ", " B B ", " C C ", " D D ", " E E ", " F F ", " G G ", " H H ", " I I ", " J J ", " K K ", " L L ", " M M ", " N N ", " O O ", " P P ", " Q Q ", " R R ", " S S ", " T T ", " U U ", " V V ", " W W ", " X X ", " Y Y ", "Z Z", " Y Y ", " X X ", " W W ", " V V ", " U U ", " T T ", " S S ", " R R ", " Q Q ", " P P ", " O O ", " N N ", " M M ", " L L ", " K K ", " J J ", " I I ", " H H ", " G G ", " F F ", " E E ", " D D ", " C C ", " B B ", " A ", ] ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { static ABC: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; pub fn get_diamond(diamond_char: char) -> Vec<String> { let mut result: Vec<String> = Vec::new(); let diamond_char = diamond_char.to_ascii_uppercase(); if ABC.find(diamond_char).is_none() { return result; } if diamond_char == 'A' { return vec![String::from("A")]; } //build first half for char_in_abc in ABC.chars() { result.push(get_line(char_in_abc, diamond_char).clone()); if char_in_abc == diamond_char { break; } } //build second half let mut rev = result.clone(); rev.pop(); //remove middle piece to avoid duplicates for line in rev.drain(..).rev() { result.push(line); } result } fn get_line(char_in_abc: char, diamond_char: char) -> String { let mut r = String::new(); let letter_e = get_letter_line(char_in_abc); let letter_c = get_letter_line(diamond_char); let ws = letter_c.len() - letter_e.len(); //number of whitespaces //left for _ in 0..ws / 2 { r.push(' '); } //letter line for i in letter_e.chars() { r.push(i) } //right for _ in 0..ws / 2 { r.push(' '); } r } fn get_letter_line(char_in_abc: char) -> String { let mut r = String::new(); let odd = (0..) .filter(|x| x % 2 != 0) .nth(ABC.find(char_in_abc).unwrap()) .unwrap(); for i in 0..odd { if i == 0 || i == odd - 1 { r.push(char_in_abc); } else { r.push(' '); } } r } #}
填充/相关
Spiral Matrix
1. Readme
螺旋矩阵
给定大小,以螺旋顺序返回数字的方阵.
矩阵应填充自然数字,从左上角的 1 开始,以向内,顺时针螺旋顺序增加,如下例所示:
大小为 3 的螺旋矩阵
1 2 3
8 9 4
7 6 5
尺寸为 4 的螺旋矩阵
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
资源
Reddit r/dailyprogrammer 挑战 #320 [简单]螺旋升天.https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/
2. 开始你的表演
pub fn spiral_matrix(size: u32) -> Vec<Vec<u32>> { unimplemented!( "Function that returns the spiral matrix of square size {}", size ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn empty_spiral() { let expected: Vec<Vec<u32>> = Vec::new(); assert_eq!(spiral_matrix(0), expected); } #[test] //#[ignore] fn size_one_spiral() { let mut expected: Vec<Vec<u32>> = Vec::new(); expected.push(vec![1]); assert_eq!(spiral_matrix(1), expected); } #[test] //#[ignore] fn size_two_spiral() { let mut expected: Vec<Vec<u32>> = Vec::new(); expected.push(vec![1, 2]); expected.push(vec![4, 3]); assert_eq!(spiral_matrix(2), expected); } #[test] //#[ignore] fn size_three_spiral() { let mut expected: Vec<Vec<u32>> = Vec::new(); expected.push(vec![1, 2, 3]); expected.push(vec![8, 9, 4]); expected.push(vec![7, 6, 5]); assert_eq!(spiral_matrix(3), expected); } #[test] //#[ignore] fn size_four_spiral() { let mut expected: Vec<Vec<u32>> = Vec::new(); expected.push(vec![1, 2, 3, 4]); expected.push(vec![12, 13, 14, 5]); expected.push(vec![11, 16, 15, 6]); expected.push(vec![10, 9, 8, 7]); assert_eq!(spiral_matrix(4), expected); } #[test] //#[ignore] fn size_five_spiral() { let mut expected: Vec<Vec<u32>> = Vec::new(); expected.push(vec![1, 2, 3, 4, 5]); expected.push(vec![16, 17, 18, 19, 6]); expected.push(vec![15, 24, 25, 20, 7]); expected.push(vec![14, 23, 22, 21, 8]); expected.push(vec![13, 12, 11, 10, 9]); assert_eq!(spiral_matrix(5), expected); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn spiral_matrix(size: u32) -> Vec<Vec<u32>> { let mut matrix: Vec<Vec<u32>> = vec![vec![0; size as usize]; size as usize]; let num_concentric_squares = (size as f64 / 2.0).ceil() as usize; let mut counter: u32 = 1; let mut sidelen = size as usize; for i in 0..num_concentric_squares { for j in 0..sidelen { matrix[i][i + j] = counter; counter += 1; } for j in 1..sidelen { matrix[i + j][size as usize - 1 - i] = counter; counter += 1; } for j in (0..sidelen - 1).rev() { matrix[size as usize - 1 - i][i + j] = counter; counter += 1; } for j in (1..sidelen - 1).rev() { matrix[i + j][i] = counter; counter += 1; } if sidelen >= 2 { sidelen -= 2; } else { sidelen -= 1; } } matrix } #}
填充/相关
Palindrome Products
1. Readme
回文乘数
在给定范围内,检测回文乘数.
回文数是指当数字倒过来时,保持不变的数。例如,121
是回文数,但112
不是。
给定一系列数字,找出最大和最小回文,这是该范围内数字的乘积.
您的解决方案应该返回最大和最小回文,以及范围内的每个因素。如果最大或最小回文在范围内,有多于一对的因素,则返回所有的对.
例 1
给定范围[1, 9]
(包含 1,9)
并给出在这个范围内的列表中,所有可能的乘数:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 15, 21, 24, 27, 20, 28, 32, 36, 25, 30, 35, 40, 45, 42, 48, 54, 49, 56, 63, 64, 72, 81]
回文乘数都是单数数字(在这种情况下):[1, 2, 3, 4, 5, 6, 7, 8, 9]
最小回文乘数是1
。 其因素是(1, 1)
。 最大回文乘数是9
。 其因素是(1, 9)
和(3, 3)
。
例 2
给定范围[10, 99]
(包含)
最小回文乘数是121
。 其因素是(11, 11)
。 最大回文乘数是9009
。 其因素是(91, 99)
。
资源
欧拉项目的问题 4http://projecteuler.net/problem=4
2. 开始你的表演
pub type Palindrome = u64; pub fn get_palindrome_products(min: u64, max: u64) -> Vec<Palindrome> { unimplemented!( "Find all palindromic numbers which are products of numbers in the inclusive range ({}..{})", min, max ) } pub fn min(palindromes: &[Palindrome]) -> Option<Palindrome> { unimplemented!( "Return the palindrome of minimal value from the supplied list: {:?}", palindromes ) } pub fn max(palindromes: &[Palindrome]) -> Option<Palindrome> { unimplemented!( "Return the palindrome of maximal value from the supplied list: {:?}", palindromes ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn single_digits() { let palindromes = get_palindrome_products(1, 9); assert_eq!(min(&palindromes), Some(1)); assert_eq!(max(&palindromes), Some(9)); } #[test] //#[ignore] fn double_digits() { let palindromes = get_palindrome_products(10, 99); assert_eq!(min(&palindromes), Some(121)); assert_eq!(max(&palindromes), Some(9009)); } #[test] //#[ignore] fn triple_digits() { let palindromes = get_palindrome_products(100, 999); assert_eq!(min(&palindromes), Some(10201)); assert_eq!(max(&palindromes), Some(906609)); } #[test] //#[ignore] fn four_digits() { let palindromes = get_palindrome_products(1000, 9999); assert_eq!(min(&palindromes), Some(1002001)); assert_eq!(max(&palindromes), Some(99000099)); } #[test] //#[ignore] fn empty_result_for_smallest_palindrome() { assert_eq!(min(&get_palindrome_products(1002, 1003)), None); } #[test] //#[ignore] fn empty_result_for_largest_palindrome() { assert_eq!(max(&get_palindrome_products(15, 15)), None); } #[test] //#[ignore] fn error_smallest_palindrome_when_min_gt_max() { assert_eq!(min(&get_palindrome_products(1000, 1)), None); } #[test] //#[ignore] fn error_largest_palindrome_when_min_st_max() { assert_eq!(max(&get_palindrome_products(2, 1)), None); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub type Palindrome = u64; pub fn get_palindrome_products(min: u64, max: u64) -> Vec<Palindrome> { let mut palindromes: Vec<u64> = Vec::new(); for i in min..max + 1 { for j in min..max + 1 { if is_palindrome(i * j) { palindromes.push(i * j); } } } palindromes } pub fn min(palindromes: &[Palindrome]) -> Option<Palindrome> { palindromes.iter().min().map(|n| n.clone()) } pub fn max(palindromes: &[Palindrome]) -> Option<Palindrome> { palindromes.iter().max().map(|n| n.clone()) } fn is_palindrome(n: u64) -> bool { let s = n.to_string().into_bytes(); s.iter().zip(s.iter().rev()).all(|(c1, c2)| c1 == c2) } #}
填充/相关
Poker
1. Readme
扑克
从扑克手牌列表中,挑选最好的手牌。
见wikipedia中的扑克手牌概述.
提示
- 排名扑克手牌可以被认为是一个排序问题.
- Rust 提供sort方法,用在
Vec<T> where T: Ord
。 Ord
types是一个总顺序形式,a < b
,a == b
或a > b
其中一个一定是真的.- 扑克手牌不符合一个总顺序:两份手牌可以不相等,但有相同的排序。例子:
3S 4S 5D 6H JH"
,"3H 4H 5C 6C JD"
。 - Rust 提供
PartialOrd
trait处理不具有完全顺序的可排序事物的情况。然而,它没有提供标准的sort
方法,用于Vec<T> where T: PartialOrd
。在这种情况下,对向量进行排序的标准方式是your_vec.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::{Less|Equal|Greater}));
这取决于你的需要.` - 您可以考虑实现了
PartialOrd
,表示扑克手牌的类型。
资源
受来自 Udacity 的培训课程的启发.https://www.udacity.com/course/viewer#!/c-cs212/
2. 开始你的表演
/// Given a list of poker hands, return a list of those hands which win. /// /// Note the type signature: this function should return _the same_ reference to /// the winning hand(s) as were passed in, not reconstructed strings which happen to be equal. pub fn winning_hands<'a>(hands: &[&'a str]) -> Option<Vec<&'a str>> { unimplemented!("Out of {:?}, which hand wins?", hands) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // use std::collections::HashSet; fn hs_from<'a>(input: &[&'a str]) -> HashSet<&'a str> { let mut hs = HashSet::new(); for item in input.iter() { hs.insert(*item); } hs } /// Test that the expected output is produced from the given input /// using the `winning_hands` function. /// /// Note that the output can be in any order. Here, we use a HashSet to /// abstract away the order of outputs. fn test<'a, 'b>(input: &[&'a str], expected: &[&'b str]) { assert_eq!( hs_from(&winning_hands(input).expect("This test should produce Some value",)), hs_from(expected) ) } #[test] fn test_single_hand_always_wins() { test(&["4S 5S 7H 8D JC"], &["4S 5S 7H 8D JC"]) } #[test] //#[ignore] fn test_highest_card_of_all_hands_wins() { test( &["4D 5S 6S 8D 3C", "2S 4C 7S 9H 10H", "3S 4S 5D 6H JH"], &["3S 4S 5D 6H JH"], ) } #[test] //#[ignore] fn test_a_tie_has_multiple_winners() { test( &[ "4D 5S 6S 8D 3C", "2S 4C 7S 9H 10H", "3S 4S 5D 6H JH", "3H 4H 5C 6C JD", ], &["3S 4S 5D 6H JH", "3H 4H 5C 6C JD"], ) } #[test] //#[ignore] fn test_high_card_can_be_low_card_in_an_otherwise_tie() { // multiple hands with the same high cards, tie compares next highest ranked, // down to last card test(&["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"], &["3S 5H 6S 8D 7H"]) } #[test] //#[ignore] fn test_one_pair_beats_high_card() { test(&["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"], &["2S 4H 6S 4D JH"]) } #[test] //#[ignore] fn test_highest_pair_wins() { test(&["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"], &["2S 4H 6C 4D JD"]) } #[test] //#[ignore] fn test_two_pairs_beats_one_pair() { test(&["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"], &["4S 5H 4C 8C 5C"]) } #[test] //#[ignore] fn test_two_pair_ranks() { // both hands have two pairs, highest ranked pair wins test(&["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"], &["2S 8H 2D 8D 3H"]) } #[test] //#[ignore] fn test_two_pairs_second_pair_cascade() { // both hands have two pairs, with the same highest ranked pair, // tie goes to low pair test(&["2S QS 2C QD JH", "JD QH JS 8D QC"], &["JD QH JS 8D QC"]) } #[test] //#[ignore] fn test_two_pairs_last_card_cascade() { // both hands have two identically ranked pairs, // tie goes to remaining card (kicker) test(&["JD QH JS 8D QC", "JS QS JC 2D QD"], &["JD QH JS 8D QC"]) } #[test] //#[ignore] fn test_three_of_a_kind_beats_two_pair() { test(&["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"], &["4S 5H 4C 8S 4H"]) } #[test] //#[ignore] fn test_three_of_a_kind_ranks() { //both hands have three of a kind, tie goes to highest ranked triplet test(&["2S 2H 2C 8D JH", "4S AH AS 8C AD"], &["4S AH AS 8C AD"]) } #[test] //#[ignore] fn test_three_of_a_kind_cascade_ranks() { // with multiple decks, two players can have same three of a kind, // ties go to highest remaining cards test(&["4S AH AS 7C AD", "4S AH AS 8C AD"], &["4S AH AS 8C AD"]) } #[test] //#[ignore] fn test_straight_beats_three_of_a_kind() { test(&["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"], &["3S 4D 2S 6D 5C"]) } #[test] //#[ignore] fn test_aces_can_end_a_straight_high() { // aces can end a straight (10 J Q K A) test(&["4S 5H 4C 8D 4H", "10D JH QS KD AC"], &["10D JH QS KD AC"]) } #[test] //#[ignore] fn test_aces_can_end_a_straight_low() { // aces can start a straight (A 2 3 4 5) test(&["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"], &["4D AH 3S 2D 5C"]) } #[test] //#[ignore] fn test_straight_cascade() { // both hands with a straight, tie goes to highest ranked card test(&["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"], &["5S 7H 8S 9D 6H"]) } #[test] //#[ignore] fn test_straight_scoring() { // even though an ace is usually high, a 5-high straight is the lowest-scoring straight test(&["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"], &["2H 3C 4D 5D 6H"]) } #[test] //#[ignore] fn test_flush_beats_a_straight() { test(&["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"], &["2S 4S 5S 6S 7S"]) } #[test] //#[ignore] fn test_flush_cascade() { // both hands have a flush, tie goes to high card, down to the last one if necessary test(&["4H 7H 8H 9H 6H", "2S 4S 5S 6S 7S"], &["4H 7H 8H 9H 6H"]) } #[test] //#[ignore] fn test_full_house_beats_a_flush() { test(&["3H 6H 7H 8H 5H", "4S 5C 4C 5D 4H"], &["4S 5C 4C 5D 4H"]) } #[test] //#[ignore] fn test_full_house_ranks() { // both hands have a full house, tie goes to highest-ranked triplet test(&["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"], &["5H 5S 5D 8S 8D"]) } #[test] //#[ignore] fn test_full_house_cascade() { // with multiple decks, both hands have a full house with the same triplet, tie goes to the pair test(&["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"], &["5H 5S 5D 9S 9D"]) } #[test] //#[ignore] fn test_four_of_a_kind_beats_full_house() { test(&["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"], &["3S 3H 2S 3D 3C"]) } #[test] //#[ignore] fn test_four_of_a_kind_ranks() { // both hands have four of a kind, tie goes to high quad test(&["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"], &["4S 5H 5S 5D 5C"]) } #[test] //#[ignore] fn test_four_of_a_kind_cascade() { // with multiple decks, both hands with identical four of a kind, tie determined by kicker test(&["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"], &["3S 3H 4S 3D 3C"]) } #[test] //#[ignore] fn test_straight_flush_beats_four_of_a_kind() { test(&["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"], &["7S 8S 9S 6S 10S"]) } #[test] //#[ignore] fn test_straight_flush_ranks() { // both hands have straight flush, tie goes to highest-ranked card test(&["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"], &["5S 7S 8S 9S 6S"]) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::cmp::Ordering; use std::fmt; #[macro_use] extern crate try_opt; extern crate counter; use counter::Counter; /// Given a list of poker hands, return a list of those hands which win. /// /// Note the type signature: this function should return _the same_ reference to /// the winning hand(s) as were passed in, not reconstructed strings which happen to be equal. pub fn winning_hands<'a>(hands: &[&'a str]) -> Option<Vec<&'a str>> { let mut hands = try_opt!( hands .iter() .map(|source| Hand::try_from(source)) .collect::<Option<Vec<Hand>>>() ); hands.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); hands.last().map(|last| { hands .iter() .rev() .take_while(|&item| item.partial_cmp(last) == Some(Ordering::Equal)) .map(|hand| hand.source) .collect() }) } #[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy, Hash)] enum Suit { Spades, Clubs, Diamonds, Hearts, } impl Suit { fn try_from(source: &str) -> Option<Suit> { use Suit::*; match source { "S" => Some(Spades), "C" => Some(Clubs), "D" => Some(Diamonds), "H" => Some(Hearts), _ => None, } } } impl fmt::Display for Suit { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Suit::*; write!( f, "{}", match *self { Spades => "S", Clubs => "C", Diamonds => "D", Hearts => "H", } ) } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] enum Rank { Number(u8), Jack, Queen, King, Ace, } impl Rank { fn try_from(source: &str) -> Option<Rank> { use Rank::*; match source { "A" => Some(Ace), "K" => Some(King), "Q" => Some(Queen), "J" => Some(Jack), "10" => Some(Number(10)), "9" => Some(Number(9)), "8" => Some(Number(8)), "7" => Some(Number(7)), "6" => Some(Number(6)), "5" => Some(Number(5)), "4" => Some(Number(4)), "3" => Some(Number(3)), "2" => Some(Number(2)), _ => None, } } fn value(&self) -> usize { use Rank::*; match *self { Ace => 14, King => 13, Queen => 12, Jack => 11, Number(n) => n as usize, } } } impl fmt::Display for Rank { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Rank::*; let num_str; // early declaration to placate NLL of Number case write!( f, "{}", match *self { Ace => "A", King => "K", Queen => "Q", Jack => "J", Number(n) => { num_str = n.to_string(); &num_str } } ) } } impl PartialOrd for Rank { fn partial_cmp(&self, other: &Rank) -> Option<Ordering> { Some(self.value().cmp(&other.value())) } } #[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)] struct Card { rank: Rank, suit: Suit, } impl Card { fn try_from_split(source: &str, split: usize) -> Option<Card> { Some(Card { rank: try_opt!(Rank::try_from(&source[..split])), suit: try_opt!(Suit::try_from(&source[split..])), }) } fn try_from(source: &str) -> Option<Card> { match source.len() { 3 => Card::try_from_split(source, 2), 2 => Card::try_from_split(source, 1), _ => None, } } } impl fmt::Display for Card { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.rank, self.suit) } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] enum PokerHand { HighCard, OnePair, TwoPair, ThreeOfAKind, Straight, Flush, FullHouse, FourOfAKind, StraightFlush, } impl PokerHand { fn is_ace_low_straight(cards: &[Card]) -> bool { // special case: ace-low straight // still depends on the sorted precondition cards[0].rank.value() == 2 && cards[4].rank == Rank::Ace && cards .windows(2) .take(3) // (0, 1), (1, 2), (2, 3) --> skips 4, ace .map(|pair| pair[1].rank.value() - pair[0].rank.value()) .all(|diff| diff == 1) } fn analyze(cards: &[Card]) -> Option<PokerHand> { if cards.len() == 5 { let suit_counter = Counter::init(cards.iter().map(|c| c.suit)); let is_flush = suit_counter .most_common() .map(|(_suit, count)| count) .next() == Some(5); // Note that `is_straight` depends on a precondition: it only works // if the input `cards` are sorted by rank value ascending. let is_straight = cards .windows(2) .map(|pair| pair[1].rank.value() - pair[0].rank.value()) .all(|diff| diff == 1) || PokerHand::is_ace_low_straight(cards); if is_flush && is_straight { return Some(PokerHand::StraightFlush); } let rank_counter = Counter::init(cards.iter().map(|c| c.rank)); let mut rc_iter = rank_counter.most_common().map(|(_rank, count)| count); let rc_most = rc_iter.next(); let rc_second = rc_iter.next(); if rc_most == Some(4) { return Some(PokerHand::FourOfAKind); } if rc_most == Some(3) && rc_second == Some(2) { return Some(PokerHand::FullHouse); } if is_flush { return Some(PokerHand::Flush); } if is_straight { return Some(PokerHand::Straight); } if rc_most == Some(3) { return Some(PokerHand::ThreeOfAKind); } if rc_most == Some(2) && rc_second == Some(2) { return Some(PokerHand::TwoPair); } if rc_most == Some(2) { return Some(PokerHand::OnePair); } Some(PokerHand::HighCard) } else { None } } } #[derive(Debug, PartialEq, Eq)] struct Hand<'a> { source: &'a str, cards: [Card; 5], hand_type: PokerHand, } impl<'a> Hand<'a> { fn try_from(source: &'a str) -> Option<Hand> { let mut cards = try_opt!( source .split_whitespace() .map(|s| Card::try_from(s)) .collect::<Option<Vec<Card>>>() ); cards.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); if cards.len() == 5 { Some(Hand { source: source, cards: [cards[0], cards[1], cards[2], cards[3], cards[4]], hand_type: try_opt!(PokerHand::analyze(&cards)), }) } else { None } } fn cmp_high_card(&self, other: &Hand, card: usize) -> Ordering { let mut ordering = self.cards[card] .rank .value() .cmp(&other.cards[card].rank.value()); if card != 0 { ordering = ordering.then_with(|| self.cmp_high_card(other, card - 1)); } ordering } fn value_by_frequency(&self) -> (Option<Rank>, Option<Rank>, Option<Rank>) { let rank_counter = Counter::init(self.cards.iter().map(|c| c.rank)); let mut rc_iter = rank_counter .most_common_tiebreaker(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Less)) .map(|(rank, _count)| rank); (rc_iter.next(), rc_iter.next(), rc_iter.next()) } fn cmp_cascade_by_freq(&self, other: &Hand) -> Ordering { let (s1, s2, s3) = self.value_by_frequency(); let (o1, o2, o3) = other.value_by_frequency(); s1.partial_cmp(&o1) .map(|c| { c.then( s2.partial_cmp(&o2) .map(|c2| c2.then(s3.partial_cmp(&o3).unwrap_or(Ordering::Equal))) .unwrap_or(Ordering::Equal), ) }) .unwrap_or(Ordering::Equal) } fn cmp_straight(&self, other: &Hand) -> Ordering { let s = if PokerHand::is_ace_low_straight(&self.cards) { 5 } else { self.cards[4].rank.value() }; let o = if PokerHand::is_ace_low_straight(&other.cards) { 5 } else { other.cards[4].rank.value() }; s.cmp(&o) } } impl<'a> fmt::Display for Hand<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.source) } } impl<'a> PartialOrd for Hand<'a> { fn partial_cmp(&self, other: &Hand) -> Option<Ordering> { Some(self.hand_type.cmp(&other.hand_type).then_with(|| { use PokerHand::*; match self.hand_type { HighCard => self.cmp_high_card(other, 4), OnePair => self.cmp_cascade_by_freq(other), TwoPair => self.cmp_cascade_by_freq(other), ThreeOfAKind => self.cmp_cascade_by_freq(other), Straight => self.cmp_straight(other), Flush => self.cmp_high_card(other, 4), FullHouse => self.cmp_cascade_by_freq(other), FourOfAKind => self.cmp_cascade_by_freq(other), StraightFlush => self.cmp_straight(other), } })) } } #}
填充/相关
Grep
1. Readme
grep
在文件中,搜索与正则表达式模式匹配的行。返回每个匹配行的行号和内容.
Unixgrep
命令可用于搜索,与用户提供的搜索查询匹配的一个或多个文件中的行(称为模式).
该grep
命令有三个参数:
- 用于匹配文件中的行的模式.
- 零个或多个标志以自定义匹配行为.
- 一个或多个要搜索匹配行的文件.
你的任务是实现grep
函数,应该读取指定文件的内容,找到与指定模式匹配的行,然后将这些行输出为单个字符串。请注意,行应按其找到的顺序输出,第一个文件中的第一个匹配行首先输出。
例如,假设有一个名为”input.txt”的文件,其中包含以下内容:
hello
world
hello again
如果我们打电话grep "hello" input.txt
,返回的字符串应该是:
hello
hello again
旗
如前所述,grep
命令还应该支持以下标志:
-n
打印每个匹配行的行号.-l
仅打印包含至少一个匹配行的文件的名称.-i
使用不区分大小写的比较匹配行.-v
反转程序 - 收集所有与模式不匹配的行.-x
仅匹配整行,而不是匹配包含匹配的行.
如果我们运行grep -n "hello" input.txt
,-n
flag 将要求匹配的行以其行号作为前缀:
1:hello
3:hello again
如果我们运行grep -i "HELLO" input.txt
,我们将做一个不区分大小写的匹配,输出将是:
hello
hello again
该grep
命令应该一次支持多个标志.
例如,运行grep -l -v "hello" file1.txt file2.txt
应该打印不包含字符串”hello”的文件的名称.
错误处理
本练习介绍了该用法failure
箱子,它为您提供了管理自定义错误类型的方法。要了解有关箱子的更多信息,请参阅failure 文档
补充阅读
虽然本练习要求您只实现最基本的grep
函数,实际上是有一个完全的,重新实现的grep
Rust 项目 -ripgrep.
如果您喜欢在 Rust 中,重写基本工具程序的概念,请务必检查以下项目:
资源
与 Nate Foster 的对话.http://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf
2. 开始你的表演
extern crate failure; use failure::Error; /// While using raw slice of str to handle flags is convenient, /// in the real-world projects it is customary to use a struct, /// that contains flags-related logic. So in this exercise /// we ask you to implement a custom struct. /// /// If you are curious about real-world implementation, refer to the `clap-rs` crate: /// https://github.com/kbknapp/clap-rs/blob/master/src/args/arg_matches.rs #[derive(Debug)] pub struct Flags; impl Flags { pub fn new(flags: &[&str]) -> Self { unimplemented!( "Given the flags {:?} implement your own 'Flags' struct to handle flags-related logic", flags ); } } pub fn grep(pattern: &str, flags: &Flags, files: &[&str]) -> Result<Vec<String>, Error> { unimplemented!( "Search the files '{:?}' for '{}' pattern and save the matches in a vector. Your search logic should be aware of the given flags '{:?}'", files, pattern, flags ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { use std::fs; static ILIAD_CONTENT: &'static str = "Achilles sing, O Goddess! Peleus' son; His wrath pernicious, who ten thousand woes Caused to Achaia's host, sent many a soul Illustrious into Ades premature, And Heroes gave (so stood the will of Jove) To dogs and to all ravening fowls a prey, When fierce dispute had separated once The noble Chief Achilles from the son Of Atreus, Agamemnon, King of men. "; static MIDSUMMER_NIGHT_CONTENT: &'static str = "I do entreat your grace to pardon me. I know not by what power I am made bold, Nor how it may concern my modesty, In such a presence here to plead my thoughts; But I beseech your grace that I may know The worst that may befall me in this case, If I refuse to wed Demetrius. "; static PARADISE_LOST_CONTENT: &'static str = "Of Mans First Disobedience, and the Fruit Of that Forbidden Tree, whose mortal tast Brought Death into the World, and all our woe, With loss of Eden, till one greater Man Restore us, and regain the blissful Seat, Sing Heav'nly Muse, that on the secret top Of Oreb, or of Sinai, didst inspire That Shepherd, who first taught the chosen Seed "; /// In The White Night /// A poem by Alexander Blok(https://en.wikipedia.org/wiki/Alexander_Blok) /// a Russian poet who is regarded as one of the most important figures of the Silver Age of Russian Poetry /// You can read the translation here: https://lyricstranslate.com/ru/белой-ночью-месяц-красный-white-night-crimson-crescent.html static IN_THE_WHITE_NIGHT_CONTENT: &'static str = "Белой ночью месяц красный Выплывает в синеве. Бродит призрачно-прекрасный, Отражается в Неве. Мне провидится и снится Исполпенье тайных дум. В вас ли доброе таится, Красный месяц, тихий шум?.. "; struct Fixture<'a> { file_names: &'a [&'a str], } impl<'a> Fixture<'a> { fn new(file_names: &'a [&'a str]) -> Self { Fixture { file_names } } fn set_up(&self) { let file_name_content_pairs = self .file_names .iter() .cloned() .map(|file_name| { if file_name.ends_with("iliad.txt") { (file_name, ILIAD_CONTENT) } else if file_name.ends_with("midsummer_night.txt") { (file_name, MIDSUMMER_NIGHT_CONTENT) } else if file_name.ends_with("paradise_lost.txt") { (file_name, PARADISE_LOST_CONTENT) } else { (file_name, IN_THE_WHITE_NIGHT_CONTENT) } }) .collect::<Vec<(&str, &str)>>(); set_up_files(&file_name_content_pairs); } } impl<'a> Drop for Fixture<'a> { fn drop(&mut self) { tear_down_files(self.file_names); } } fn set_up_files(files: &[(&str, &str)]) { for (file_name, file_content) in files { fs::write(file_name, file_content).expect(&format!( "Error setting up file '{}' with the following content:\n{}", file_name, file_content )); } } fn tear_down_files(files: &[&str]) { for file_name in files { fs::remove_file(file_name).expect(&format!("Could not delete file '{}'", file_name)); } } /// This macro is here so that every test case had its own set of files to be used in test. /// The approach is to create required files for every test case and to append test name to the /// file names (so for test with a name 'test_one_file_one_match_no_flags' and a required file /// 'iliad.txt' there would be created a file with a name /// 'test_one_file_one_match_no_flags_iliad.txt'). /// This allows us to create files for every test case with no intersection between them. /// /// A better way would be to create required set of files at the start of tests run and to /// delete them after every test is finished, but there is no trivial way to create such /// a test fixture in standard Rust, and Exercism restricts the usage of external dependencies /// in test files. Therefore the above approach is chosen. /// /// If you have an idea about a better way to implement test fixture for this exercise, /// please submit PR to the Rust Exercism track: https://github.com/exercism/rust macro_rules! set_up_test_case { ($(#[$flag:meta])+ $test_case_name:ident(pattern=$pattern:expr, flags=[$($grep_flag:expr),*], files=[$($file:expr),+], expected=[$($expected:expr),*])) => { $(#[$flag])+ fn $test_case_name() { let pattern = $pattern; let flags = vec![$($grep_flag),*]; let files = vec![$(concat!(stringify!($test_case_name), "_" , $file)),+]; let expected = vec![$($expected),*]; process_grep_case(&pattern, &flags, &files, &expected); } }; ($(#[$flag:meta])+ $test_case_name:ident(pattern=$pattern:expr, flags=[$($grep_flag:expr),*], files=[$($file:expr),+], prefix_expected=[$($expected:expr),*])) => { $(#[$flag])+ fn $test_case_name() { let pattern = $pattern; let flags = vec![$($grep_flag),*]; let files = vec![$(concat!(stringify!($test_case_name), "_" , $file)),+]; let expected = vec![$(concat!(stringify!($test_case_name), "_", $expected)),*]; process_grep_case(&pattern, &flags, &files, &expected); } } } fn process_grep_case(pattern: &str, flags: &[&str], files: &[&str], expected: &[&str]) { let test_fixture = Fixture::new(files); test_fixture.set_up(); let flags = Flags::new(flags); let grep_result = grep(pattern, &flags, files).unwrap(); assert_eq!(grep_result, expected); } // Test returning a Result #[test] fn test_nonexistent_file_returns_error() { let pattern = "Agamemnon"; let flags = Flags::new(&vec![]); let files = vec!["test_nonexistent_file_returns_error_iliad.txt"]; assert!(grep(&pattern, &flags, &files).is_err()); } #[test] //#[ignore] fn test_grep_returns_result() { let pattern = "Agamemnon"; let flags = Flags::new(&vec![]); let files = vec!["test_grep_returns_result_iliad.txt"]; let test_fixture = Fixture::new(&files); test_fixture.set_up(); assert!(grep(&pattern, &flags, &files).is_ok()); } // Test grepping a single file set_up_test_case!(#[test] //#[ignore] test_one_file_one_match_no_flags( pattern = "Agamemnon", flags = [], files = ["iliad.txt"], expected = ["Of Atreus, Agamemnon, King of men."] )); set_up_test_case!(#[test] //#[ignore] test_one_file_one_match_print_line_numbers_flag( pattern = "Forbidden", flags = ["-n"], files = ["paradise_lost.txt"], expected = ["2:Of that Forbidden Tree, whose mortal tast"] )); set_up_test_case!(#[test] //#[ignore] test_one_file_one_match_caseinsensitive_flag( pattern = "FORBIDDEN", flags = ["-i"], files = ["paradise_lost.txt"], expected = ["Of that Forbidden Tree, whose mortal tast"] )); set_up_test_case!(#[test] //#[ignore] test_one_file_one_match_print_file_names_flag( pattern = "Forbidden", flags = ["-l"], files = ["paradise_lost.txt"], prefix_expected = ["paradise_lost.txt"] )); set_up_test_case!(#[test] //#[ignore] test_one_file_one_match_match_entire_lines_flag( pattern = "With loss of Eden, till one greater Man", flags = ["-x"], files = ["paradise_lost.txt"], expected = ["With loss of Eden, till one greater Man"] )); set_up_test_case!(#[test] //#[ignore] test_one_file_one_match_multiple_flags( pattern = "OF ATREUS, Agamemnon, KIng of MEN.", flags = ["-x", "-i", "-n"], files = ["iliad.txt"], expected = ["9:Of Atreus, Agamemnon, King of men."] )); set_up_test_case!(#[test] //#[ignore] test_one_file_several_matches_no_flags( pattern = "may", flags = [], files = ["midsummer_night.txt"], expected = [ "Nor how it may concern my modesty,", "But I beseech your grace that I may know", "The worst that may befall me in this case," ] )); set_up_test_case!(#[test] //#[ignore] test_one_file_several_matches_print_line_numbers_flag( pattern = "may", flags = ["-n"], files = ["midsummer_night.txt"], expected = [ "3:Nor how it may concern my modesty,", "5:But I beseech your grace that I may know", "6:The worst that may befall me in this case," ] )); set_up_test_case!(#[test] //#[ignore] test_one_file_several_matches_match_entire_lines_flag( pattern = "may", flags = ["-x"], files = ["midsummer_night.txt"], expected = [] )); set_up_test_case!(#[test] //#[ignore] test_one_file_several_matches_caseinsensitive_flag( pattern = "ACHILLES", flags = ["-i"], files = ["iliad.txt"], expected = [ "Achilles sing, O Goddess! Peleus' son;", "The noble Chief Achilles from the son" ] )); set_up_test_case!(#[test] //#[ignore] test_one_file_several_matches_inverted_flag( pattern = "Of", flags = ["-v"], files = ["paradise_lost.txt"], expected = [ "Brought Death into the World, and all our woe,", "With loss of Eden, till one greater Man", "Restore us, and regain the blissful Seat,", "Sing Heav'nly Muse, that on the secret top", "That Shepherd, who first taught the chosen Seed" ] )); set_up_test_case!(#[test] //#[ignore] test_one_file_no_matches_various_flags( pattern = "Gandalf", flags = ["-n", "-l", "-x", "-i"], files = ["iliad.txt"], expected = [] )); // Test grepping multiples files at once set_up_test_case!(#[test] //#[ignore] test_multiple_files_one_match_no_flags( pattern = "Agamemnon", flags = [], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = ["iliad.txt:Of Atreus, Agamemnon, King of men."] )); set_up_test_case!(#[test] //#[ignore] test_multiple_files_several_matches_no_flags( pattern = "may", flags = [], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = [ "midsummer_night.txt:Nor how it may concern my modesty,", "midsummer_night.txt:But I beseech your grace that I may know", "midsummer_night.txt:The worst that may befall me in this case," ] )); set_up_test_case!(#[test] //#[ignore] test_multiple_files_several_matches_print_line_numbers_flag( pattern = "that", flags = ["-n"], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = [ "midsummer_night.txt:5:But I beseech your grace that I may know", "midsummer_night.txt:6:The worst that may befall me in this case,", "paradise_lost.txt:2:Of that Forbidden Tree, whose mortal tast", "paradise_lost.txt:6:Sing Heav'nly Muse, that on the secret top" ] )); set_up_test_case!(#[test] //#[ignore] test_multiple_files_one_match_print_file_names_flag( pattern = "who", flags = ["-l"], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = ["iliad.txt", "paradise_lost.txt"] )); set_up_test_case!(#[test] //#[ignore] test_multiple_files_several_matches_caseinsensitive_flag( pattern = "TO", flags = ["-i"], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = [ "iliad.txt:Caused to Achaia's host, sent many a soul", "iliad.txt:Illustrious into Ades premature,", "iliad.txt:And Heroes gave (so stood the will of Jove)", "iliad.txt:To dogs and to all ravening fowls a prey,", "midsummer_night.txt:I do entreat your grace to pardon me.", "midsummer_night.txt:In such a presence here to plead my thoughts;", "midsummer_night.txt:If I refuse to wed Demetrius.", "paradise_lost.txt:Brought Death into the World, and all our woe,", "paradise_lost.txt:Restore us, and regain the blissful Seat,", "paradise_lost.txt:Sing Heav'nly Muse, that on the secret top" ] )); set_up_test_case!( #[test] //#[ignore] test_multiple_files_several_matches_caseinsensitive_flag_utf8( pattern = "В", // This letter stands for cyrillic 'Ve' and not latin 'B'. Therefore there should be no matches from paradise_lost.txt flags = ["-i"], files = ["paradise_lost.txt", "in_the_white_night.txt"], prefix_expected = [ "in_the_white_night.txt:Выплывает в синеве.", "in_the_white_night.txt:Отражается в Неве.", "in_the_white_night.txt:Мне провидится и снится", "in_the_white_night.txt:В вас ли доброе таится," ] ) ); set_up_test_case!(#[test] //#[ignore] test_multiple_files_several_matches_inverted_flag( pattern = "a", flags = ["-v"], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = [ "iliad.txt:Achilles sing, O Goddess! Peleus' son;", "iliad.txt:The noble Chief Achilles from the son", "midsummer_night.txt:If I refuse to wed Demetrius." ] )); set_up_test_case!(#[test] //#[ignore] test_multiple_files_one_match_match_entire_lines_flag( pattern = "But I beseech your grace that I may know", flags = ["-x"], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = ["midsummer_night.txt:But I beseech your grace that I may know"] )); set_up_test_case!(#[test] //#[ignore] test_multiple_files_one_match_multiple_flags( pattern = "WITH LOSS OF EDEN, TILL ONE GREATER MAN", flags = ["-n", "-i", "-x"], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], prefix_expected = ["paradise_lost.txt:4:With loss of Eden, till one greater Man"] )); set_up_test_case!(#[test] //#[ignore] test_multiple_files_no_matches_various_flags( pattern = "Frodo", flags = ["-n", "-i", "-x", "-l"], files = ["iliad.txt", "midsummer_night.txt", "paradise_lost.txt"], expected = [] )); #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[macro_use] extern crate failure; use failure::Error; use std::{fs, path::Path}; #[derive(Debug, Fail)] enum FileAccessError { #[fail(display = "File not found: {}", file_name)] FileNotFoundError { file_name: String }, #[fail(display = "Error reading file: {}", file_name)] FileReadError { file_name: String }, } pub struct Flags { print_line_number: bool, print_file_name: bool, use_caseinsensitive_comparison: bool, use_inverted_comparison: bool, match_entire_line: bool, } impl Flags { pub fn new(flags: &[&str]) -> Self { Flags { print_line_number: flags.contains(&"-n"), print_file_name: flags.contains(&"-l"), use_caseinsensitive_comparison: flags.contains(&"-i"), use_inverted_comparison: flags.contains(&"-v"), match_entire_line: flags.contains(&"-x"), } } } fn get_file_lines(file_name: &str) -> Result<Vec<String>, FileAccessError> { let file_path = Path::new(file_name); if !file_path.exists() { return Err(FileAccessError::FileNotFoundError { file_name: String::from(file_name), }); } if let Ok(content) = fs::read_to_string(file_path) { Ok(content.split("\n").map(|line| line.to_string()).collect()) } else { Err(FileAccessError::FileReadError { file_name: String::from(file_name), }) } } pub fn grep(pattern: &str, flags: &Flags, files: &[&str]) -> Result<Vec<String>, Error> { let mut grep_result = vec![]; let is_multiple_file_search = files.len() > 1; for file_name in files { let file_lines = get_file_lines(file_name)?; grep_result.extend( file_lines .iter() .enumerate() .filter(|&(_, line)| { let mut inner_line = String::from(line.clone()); let mut inner_pattern = String::from(pattern); if flags.use_caseinsensitive_comparison { inner_line = inner_line.to_lowercase().to_string(); inner_pattern = inner_pattern.to_lowercase().to_string(); } let mut is_filtered = inner_line.contains(&inner_pattern); if flags.match_entire_line { is_filtered = inner_line == inner_pattern; } if flags.use_inverted_comparison { is_filtered = !inner_line.contains(&inner_pattern); } is_filtered }) .filter(|(_, line)| !line.is_empty()) .map(|(line_number, line)| { let mut result = line.to_owned(); if flags.print_line_number { result.insert_str(0, &format!("{}:", line_number + 1)); } if is_multiple_file_search { result.insert_str(0, &format!("{}:", file_name)) } if flags.print_file_name { result = file_name.to_owned().to_owned(); } result }), ); } grep_result.dedup_by(|a, b| (*a).eq(b)); Ok(grep_result) } #}
填充/相关
Scale Generator
1. Readme
音阶生成器
译:真不知道说什么。
给出一个音调,或是开始的音符以及一组间隔,从音调开始并遵循指定的间隔模式生成音阶。
西方音乐中的音阶,基于彩色(12 音符)音阶。该音阶可表示为以下一组音高:
A,A#,B,C,C#,D,D#,E,F,F#,G,G#
给出一个尖音符(用#表示)也可以表示为它前面的平音符(用 b 表示),所以半音音阶也可以这样写:
A,Bb,B,C,Db,D,Eb,E,F,Gb,G,Ab
大调和小调的音阶和模式是这个十二音高集合的子集。它们有七个音高,称为全音阶音阶。这些音阶中的音符集合使用锐利或平面,根据音调。这是一个列表,其中包括:
没有尖(Sharps)或平(Flats):C 大调(major)和 a 小调(minor)
使用 Sharps: G, D, A, E, B, F# major e, b, f#, c#, g#, d# minor
使用 Flats: F, Bb, Eb, Ab, Db, Gb major d, g, c, f, bb, eb minor
全音阶音阶以及源自半音音阶的所有其他音阶都是间隔建立的。间隔是两个音高之间的间距。
最简单的间隔是在两个相邻音符之间,称为”半步”或”小秒”(有时写为小写”m”)。具有中间音符的两个音符之间的间隔称为”整步”或”大秒”(写为大写”M”)。仅使用相邻音符之间的这两个间隔,来构建全音阶音阶。
非全音阶音阶可以包含其他间隔。一个”先增强”的间隔,写上 A,具有两个中间音符(例如,从 A 到 C 或从 D 到 E)。间隔也越来越小,但他们不会参与这个练习。
2. 开始你的表演
// You should change this. // // Depending on your implementation, there are a variety of potential errors // which might occur. They aren't checked by the test suite in order to // allow the greatest freedom of implementation, but real libraries should // provide useful, descriptive errors so that downstream code can react // appropriately. // // One common idiom is to define an Error enum which wraps all potential // errors. Another common idiom is to use a helper type such as failure::Error // which does more or less the same thing but automatically. pub type Error = (); pub struct Scale; impl Scale { pub fn new(tonic: &str, intervals: &str) -> Result<Scale, Error> { unimplemented!( "Construct a new scale with tonic {} and intervals {}", tonic, intervals ) } pub fn chromatic(tonic: &str) -> Result<Scale, Error> { unimplemented!("Construct a new chromatic scale with tonic {}", tonic) } pub fn enumerate(&self) -> Vec<String> { unimplemented!() } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { /// Tests for scale-generator /// /// Generated by [script][script] using [canonical data][canonical-data] /// /// [script]: https://github.com/exercism/rust/blob/master/bin/init_exercise.py /// [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/scale-generator/canonical_data.json /// Process a single test case for the property `chromatic` /// /// All cases for the `chromatic` property are implemented /// in terms of this function. fn process_chromatic_case(tonic: &str, expected: &[&str]) { let s = Scale::chromatic(tonic).unwrap(); assert_eq!(s.enumerate(), expected); } /// Process a single test case for the property `interval` /// /// All cases for the `interval` property are implemented /// in terms of this function. fn process_interval_case(tonic: &str, intervals: &str, expected: &[&str]) { let s = Scale::new(tonic, intervals).unwrap(); assert_eq!(s.enumerate(), expected); } // Chromatic scales // These tests have no interval. // The chromatic scale is considered the default scale #[test] /// Chromatic scale with sharps fn test_chromatic_scale_with_sharps() { process_chromatic_case( "C", &[ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", ], ); } #[test] //#[ignore] /// Chromatic scale with flats fn test_chromatic_scale_with_flats() { process_chromatic_case( "F", &[ "F", "Gb", "G", "Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E", ], ); } // Scales with specified intervals // These tests all have intervals and are explorations of different // traversals of the scale. #[test] //#[ignore] /// Simple major scale /// /// The simplest major scale, with no sharps or flats. fn test_simple_major_scale() { process_interval_case("C", "MMmMMMm", &["C", "D", "E", "F", "G", "A", "B"]); } #[test] //#[ignore] /// Major scale with sharps fn test_major_scale_with_sharps() { process_interval_case("G", "MMmMMMm", &["G", "A", "B", "C", "D", "E", "F#"]); } #[test] //#[ignore] /// Major scale with flats fn test_major_scale_with_flats() { process_interval_case("F", "MMmMMMm", &["F", "G", "A", "Bb", "C", "D", "E"]); } #[test] //#[ignore] /// Minor scale with sharps fn test_minor_scale_with_sharps() { process_interval_case("f#", "MmMMmMM", &["F#", "G#", "A", "B", "C#", "D", "E"]); } #[test] //#[ignore] /// Minor scale with flats fn test_minor_scale_with_flats() { process_interval_case("bb", "MmMMmMM", &["Bb", "C", "Db", "Eb", "F", "Gb", "Ab"]); } #[test] //#[ignore] /// Dorian mode fn test_dorian_mode() { process_interval_case("d", "MmMMMmM", &["D", "E", "F", "G", "A", "B", "C"]); } #[test] //#[ignore] /// Mixolydian mode fn test_mixolydian_mode() { process_interval_case("Eb", "MMmMMmM", &["Eb", "F", "G", "Ab", "Bb", "C", "Db"]); } #[test] //#[ignore] /// Lydian mode fn test_lydian_mode() { process_interval_case("a", "MMMmMMm", &["A", "B", "C#", "D#", "E", "F#", "G#"]); } #[test] //#[ignore] /// Phrygian mode fn test_phrygian_mode() { process_interval_case("e", "mMMMmMM", &["E", "F", "G", "A", "B", "C", "D"]); } #[test] //#[ignore] /// Locrian mode fn test_locrian_mode() { process_interval_case("g", "mMMmMMM", &["G", "Ab", "Bb", "C", "Db", "Eb", "F"]); } #[test] //#[ignore] /// Harmonic minor /// /// Note that this case introduces the accidental interval (A) fn test_harmonic_minor() { process_interval_case("d", "MmMMmAm", &["D", "E", "F", "G", "A", "Bb", "Db"]); } #[test] //#[ignore] /// Octatonic fn test_octatonic() { process_interval_case( "C", "MmMmMmMm", &["C", "D", "D#", "F", "F#", "G#", "A", "B"], ); } #[test] //#[ignore] /// Hexatonic fn test_hexatonic() { process_interval_case("Db", "MMMMMM", &["Db", "Eb", "F", "G", "A", "B"]); } #[test] //#[ignore] /// Pentatonic fn test_pentatonic() { process_interval_case("A", "MMAMA", &["A", "B", "C#", "E", "F#"]); } #[test] //#[ignore] /// Enigmatic fn test_enigmatic() { process_interval_case("G", "mAMMMmm", &["G", "G#", "B", "C#", "D#", "F", "F#"]); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[macro_use] extern crate enum_primitive_derive; extern crate failure; #[macro_use] extern crate failure_derive; extern crate itertools; extern crate num_traits; pub use self::interval::{Interval, Intervals}; use self::note::Accidental; pub use self::note::Note; use failure::Error; use std::str::FromStr; pub mod interval { use itertools::Itertools; use std::fmt; use std::ops::Deref; use std::str::FromStr; #[derive(Debug, Clone, Copy, PartialEq, Eq, Fail)] pub enum ParseErr { #[fail(display = "invalid interval")] InvalidInterval, #[fail(display = "wrong number of semitones")] WrongNumberOfSemitones, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Primitive)] pub enum Interval { HalfStep = 1, WholeStep = 2, AugmentedFirst = 3, } impl fmt::Display for Interval { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Interval::*; write!( f, "{}", match self { HalfStep => "m", WholeStep => "M", AugmentedFirst => "A", } ) } } impl FromStr for Interval { type Err = ParseErr; fn from_str(s: &str) -> Result<Self, Self::Err> { use self::Interval::*; match s { "m" => Ok(HalfStep), "M" => Ok(WholeStep), "A" => Ok(AugmentedFirst), _ => Err(ParseErr::InvalidInterval), } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Intervals(Vec<Interval>); impl fmt::Display for Intervals { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0.iter().join("")) } } impl FromStr for Intervals { type Err = ParseErr; fn from_str(s: &str) -> Result<Self, Self::Err> { let mut semitones = Vec::with_capacity(s.len()); for (i, c) in s.char_indices() { semitones.push(Interval::from_str(&s[i..i + c.len_utf8()])?); } if semitones.iter().take(12).map(|&i| i as u8).sum::<u8>() == 12 { Ok(Intervals(semitones)) } else { Err(ParseErr::WrongNumberOfSemitones) } } } impl Deref for Intervals { type Target = Vec<Interval>; fn deref(&self) -> &Self::Target { &self.0 } } #[cfg(test)] mod test { use super::*; #[test] fn test_parse_chromatic() { assert!("mmmmmmmmmmmm".parse::<Intervals>().is_ok()); } #[test] fn test_parse_major() { assert!("MMmMMMm".parse::<Intervals>().is_ok()); } #[test] fn test_parse_minor() { assert!("MmMMmMM".parse::<Intervals>().is_ok()); } } } pub mod note { use interval::Interval; use num_traits::{FromPrimitive, ToPrimitive}; use std::fmt; use std::ops::AddAssign; use std::str::FromStr; pub const SEMITONES: i8 = 12; #[derive(Debug, Clone, Copy, PartialEq, Eq, Primitive)] pub enum Semitone { A = 0, ASharp = 1, B = 2, C = 3, CSharp = 4, D = 5, DSharp = 6, E = 7, F = 8, FSharp = 9, G = 10, GSharp = 11, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Primitive)] pub enum Root { A = 0, B = 2, C = 3, D = 5, E = 7, F = 8, G = 10, } impl fmt::Display for Root { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Accidental { Sharp, Flat, } impl Accidental { fn to_i8(&self) -> i8 { match *self { Accidental::Sharp => 1, Accidental::Flat => -1, } } pub fn from_tonic(tonic: &str) -> Accidental { match tonic { "C" | "a" | "G" | "D" | "A" | "E" | "B" | "F#" | "e" | "b" | "f#" | "c#" | "g#" | "d#" => Accidental::Sharp, _ => Accidental::Flat, } } } impl fmt::Display for Accidental { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", match &self { Accidental::Sharp => '#', Accidental::Flat => 'b', } ) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Note { tonic: Root, accidental: Option<Accidental>, } impl Note { pub fn canonicalize(&self, lean: Accidental) -> Note { let mut n: Note = Semitone::from(*self).into(); if let Some(accidental) = n.accidental { if accidental != lean { if lean == Accidental::Flat { n += Interval::HalfStep; n.accidental = Some(Accidental::Flat); } } } n } } impl fmt::Display for Note { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}{}", self.tonic, self.accidental.map_or(String::new(), |a| a.to_string()), ) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Fail)] pub enum ParseErr { #[fail(display = "invalid length")] InvalidLength, #[fail(display = "invalid tonic")] InvalidTonic, #[fail(display = "invalid accidental")] InvalidAccidental, } impl FromStr for Note { type Err = ParseErr; fn from_str(s: &str) -> Result<Self, Self::Err> { let lc = s.to_lowercase(); let mut iter = lc.chars(); let mut note = match iter.next() { Some(c) if 'a' <= c && 'g' >= c => Note { tonic: match c { 'a' => Root::A, 'b' => Root::B, 'c' => Root::C, 'd' => Root::D, 'e' => Root::E, 'f' => Root::F, 'g' => Root::G, _ => return Err(ParseErr::InvalidTonic), }, accidental: None, }, Some(_) => return Err(ParseErr::InvalidTonic), None => return Err(ParseErr::InvalidLength), }; match iter.next() { Some('b') => note.accidental = Some(Accidental::Flat), Some('#') => note.accidental = Some(Accidental::Sharp), Some(_) => return Err(ParseErr::InvalidAccidental), None => {} } if iter.next().is_some() { return Err(ParseErr::InvalidLength); } Ok(note) } } impl From<Semitone> for Note { fn from(s: Semitone) -> Self { Note { tonic: match s { Semitone::A | Semitone::ASharp => Root::A, Semitone::B => Root::B, Semitone::C | Semitone::CSharp => Root::C, Semitone::D | Semitone::DSharp => Root::D, Semitone::E => Root::E, Semitone::F | Semitone::FSharp => Root::F, Semitone::G | Semitone::GSharp => Root::G, }, accidental: match s { Semitone::ASharp | Semitone::CSharp | Semitone::DSharp | Semitone::FSharp | Semitone::GSharp => Some(Accidental::Sharp), _ => None, }, } } } impl From<Note> for Semitone { fn from(n: Note) -> Self { Semitone::from_i8( (SEMITONES + n.tonic.to_i8().unwrap() + n.accidental.map_or(0, |a| a.to_i8())) % SEMITONES, ).expect("must have valid semitone") } } impl AddAssign<Interval> for Note { fn add_assign(&mut self, rhs: Interval) { *self = Semitone::from_i8( (SEMITONES + Semitone::from(*self).to_i8().unwrap() + rhs.to_i8().unwrap()) % SEMITONES, ).unwrap() .into(); } } } #[derive(Debug)] pub struct Scale { tonic: Note, lean: Accidental, intervals: Intervals, } impl Scale { pub fn new(tonic: &str, intervals: &str) -> Result<Scale, Error> { Ok(Scale { tonic: Note::from_str(tonic)?, lean: Accidental::from_tonic(tonic), intervals: Intervals::from_str(intervals)?, }) } pub fn chromatic(tonic: &str) -> Result<Scale, Error> { Scale::new(tonic, "mmmmmmmmmmmm") } pub fn enumerate(&self) -> Vec<String> { let mut out = Vec::with_capacity(self.intervals.len()); let mut note = self.tonic; for &interval in self.intervals.iter() { out.push(note.canonicalize(self.lean).to_string()); note += interval; } out } } #}
填充/相关
Decimal
1. Readme
十进制
实现任意精度的Decimal
类。
浮点数是计算中非整数实数的最常见表示,它们是由IEEE 754标准定义。它们非常灵活且通用,但它们确实有一些局限性。众所周知,在浮点运算中,0.1 + 0.2 != 0.3
。
解决这一问题的方法是,寻找另一种无损的方法来模拟任意精度的非整数 实数。这可能在内存或处理速度方面,不如浮点数有效;但目标是提供准确的结果。
尽管Decimal
作为一种自定义类型,我们仍然应该能够将它们视为数字: 而==
,<
,>
,+
,-
和*
操作符都应该按小数进行工作。只是权宜之计,你不需要执行除法,因为任意的精确除法很快就会失控。(如何表示任意精度1/3
?)
在 Rust 中,将这些操作用于自定义类型的方法是,实现自定义对象的相关 trait。特别是,您至少需要实现.PartialEq
,PartialOrd
,Add
,Sub
和Mul
。 严格地说,由于十进制数构成一个总排序,你也应该实现Eq
和Ord
,尽管这些 trait 并没有被这些测试所检验.
笔记
使用这种.bigdecimal箱子方法很容易实现这个练习。但不要那样做,你自己来实现。
提示
- 不要从头开始执行任意精确的算术,而是考虑在num_bigint箱子之上构建自己的类。
- 你也许能derive一些需要的 trait。
Decimal
假设为符号类型。你不必创建一个单独的无符号类型,尽管你可以这样做,如果你选择这么样,能作为一个实现细节。
资源
Peter Goodspeed-Niklaus
2. 开始你的表演
/// Type implementing arbitrary-precision decimal arithmetic pub struct Decimal { // implement your type here } impl Decimal { pub fn try_from(input: &str) -> Option<Decimal> { unimplemented!("Create a new decimal with a value of {}", input) } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { /// Create a Decimal from a string literal /// /// Use only when you _know_ that your value is valid. fn decimal(input: &str) -> Decimal { Decimal::try_from(input).expect("That was supposed to be a valid value") } /// Some big and precise values we can use for testing. [0] + [1] == [2] const BIGS: [&'static str; 3] = [ "100000000000000000000000000000000000000000000.00000000000000000000000000000000000000001", "100000000000000000000000000000000000000000000.00000000000000000000000000000000000000002", "200000000000000000000000000000000000000000000.00000000000000000000000000000000000000003", ]; // test simple properties of required operations #[test] fn test_eq() { assert!(decimal("0.0") == decimal("0.0")); assert!(decimal("1.0") == decimal("1.0")); for big in BIGS.iter() { assert!(decimal(big) == decimal(big)); } } #[test] //#[ignore] fn test_ne() { assert!(decimal("0.0") != decimal("1.0")); assert!(decimal(BIGS[0]) != decimal(BIGS[1])); } #[test] //#[ignore] fn test_gt() { for slice_2 in BIGS.windows(2) { assert!(decimal(slice_2[1]) > decimal(slice_2[0])); assert!(!(decimal(slice_2[0]) > decimal(slice_2[1]))); } } #[test] //#[ignore] fn test_lt() { for slice_2 in BIGS.windows(2) { assert!(decimal(slice_2[0]) < decimal(slice_2[1])); assert!(!(decimal(slice_2[1]) < decimal(slice_2[0]))); } } #[test] //#[ignore] fn test_add() { assert_eq!(decimal("0.1") + decimal("0.2"), decimal("0.3")); assert_eq!(decimal(BIGS[0]) + decimal(BIGS[1]), decimal(BIGS[2])); assert_eq!(decimal(BIGS[1]) + decimal(BIGS[0]), decimal(BIGS[2])); } #[test] //#[ignore] fn test_sub() { assert_eq!(decimal(BIGS[2]) - decimal(BIGS[1]), decimal(BIGS[0])); assert_eq!(decimal(BIGS[2]) - decimal(BIGS[0]), decimal(BIGS[1])); } #[test] //#[ignore] fn test_mul() { for big in BIGS.iter() { assert_eq!(decimal(big) * decimal("2"), decimal(big) + decimal(big)); } } // test identities #[test] //#[ignore] fn test_add_id() { assert_eq!(decimal("1.0") + decimal("0.0"), decimal("1.0")); assert_eq!(decimal("0.1") + decimal("0.0"), decimal("0.1")); assert_eq!(decimal("0.0") + decimal("1.0"), decimal("1.0")); assert_eq!(decimal("0.0") + decimal("0.1"), decimal("0.1")); } #[test] //#[ignore] fn test_sub_id() { assert_eq!(decimal("1.0") - decimal("0.0"), decimal("1.0")); assert_eq!(decimal("0.1") - decimal("0.0"), decimal("0.1")); } #[test] //#[ignore] fn test_mul_id() { assert_eq!(decimal("2.1") * decimal("1.0"), decimal("2.1")); assert_eq!(decimal("1.0") * decimal("2.1"), decimal("2.1")); } #[test] //#[ignore] fn test_gt_positive_and_zero() { assert!(decimal("1.0") > decimal("0.0")); assert!(decimal("0.1") > decimal("0.0")); } #[test] //#[ignore] fn test_gt_negative_and_zero() { assert!(decimal("0.0") > decimal("-0.1")); assert!(decimal("0.0") > decimal("-1.0")); } // tests of arbitrary precision behavior #[test] //#[ignore] fn test_add_uneven_position() { assert_eq!(decimal("0.1") + decimal("0.02"), decimal("0.12")); } #[test] //#[ignore] fn test_eq_vary_sig_digits() { assert!(decimal("0") == decimal("0000000000000.0000000000000000000000")); assert!(decimal("1") == decimal("00000000000000001.000000000000000000")); } #[test] //#[ignore] fn test_add_vary_precision() { assert_eq!( decimal("100000000000000000000000000000000000000000000") + decimal("0.00000000000000000000000000000000000000001"), decimal(BIGS[0]) ) } #[test] //#[ignore] fn test_cleanup_precision() { assert_eq!( decimal("10000000000000000000000000000000000000000000000.999999999999999999999999998",) + decimal( "10000000000000000000000000000000000000000000000.000000000000000000000000002", ), decimal("20000000000000000000000000000000000000000000001") ) } #[test] //#[ignore] fn test_gt_varying_positive_precisions() { assert!(decimal("1.1") > decimal("1.01")); assert!(decimal("1.01") > decimal("1.0")); assert!(decimal("1.0") > decimal("0.1")); assert!(decimal("0.1") > decimal("0.01")); } #[test] //#[ignore] fn test_gt_positive_and_negative() { assert!(decimal("1.0") > decimal("-1.0")); assert!(decimal("1.1") > decimal("-1.1")); assert!(decimal("0.1") > decimal("-0.1")); } #[test] //#[ignore] fn test_gt_varying_negative_precisions() { assert!(decimal("-0.01") > decimal("-0.1")); assert!(decimal("-0.1") > decimal("-1.0")); assert!(decimal("-1.0") > decimal("-1.01")); assert!(decimal("-1.01") > decimal("-1.1")); } // test signed properties #[test] //#[ignore] fn test_negatives() { assert!(Decimal::try_from("-1").is_some()); assert_eq!(decimal("0") - decimal("1"), decimal("-1")); assert_eq!(decimal("5.5") + decimal("-6.5"), decimal("-1")); } #[test] //#[ignore] fn test_explicit_positive() { assert_eq!(decimal("+1"), decimal("1")); assert_eq!(decimal("+2.0") - decimal("-0002.0"), decimal("4")); } #[test] //#[ignore] fn test_multiply_by_negative() { assert_eq!(decimal("5") * decimal("-0.2"), decimal("-1")); assert_eq!(decimal("-20") * decimal("-0.2"), decimal("4")); } #[test] //#[ignore] fn test_simple_partial_cmp() { assert!(decimal("1.0") < decimal("1.1")); assert!(decimal("0.00000000000000000000001") > decimal("-20000000000000000000000000000")); } // test carrying rules // these tests are designed to ensure correctness of implementations for which the // integer and fractional parts of the number are stored separately #[test] //#[ignore] fn test_carry_into_integer() { assert_eq!(decimal("0.901") + decimal("0.1"), decimal("1.001")) } #[test] //#[ignore] fn test_carry_into_fractional_with_digits_to_right() { assert_eq!(decimal("0.0901") + decimal("0.01"), decimal("0.1001")) } #[test] //#[ignore] fn test_add_carry_over_negative() { assert_eq!(decimal("-1.99") + decimal("-0.01"), decimal("-2.0")) } #[test] //#[ignore] fn test_sub_carry_over_negative() { assert_eq!(decimal("-1.99") - decimal("0.01"), decimal("-2.0")) } #[test] //#[ignore] fn test_add_carry_over_negative_with_fractional() { assert_eq!(decimal("-1.99") + decimal("-0.02"), decimal("-2.01")) } #[test] //#[ignore] fn test_sub_carry_over_negative_with_fractional() { assert_eq!(decimal("-1.99") - decimal("0.02"), decimal("-2.01")) } #[test] //#[ignore] fn test_carry_from_rightmost_one() { assert_eq!(decimal("0.09") + decimal("0.01"), decimal("0.1")) } #[test] //#[ignore] fn test_carry_from_rightmost_more() { assert_eq!(decimal("0.099") + decimal("0.001"), decimal("0.1")) } #[test] //#[ignore] fn test_carry_from_rightmost_into_integer() { assert_eq!(decimal("0.999") + decimal("0.001"), decimal("1.0")) } // test arithmetic borrow rules #[test] //#[ignore] fn test_add_borrow() { assert_eq!(decimal("0.01") + decimal("-0.0001"), decimal("0.0099")) } #[test] //#[ignore] fn test_sub_borrow() { assert_eq!(decimal("0.01") - decimal("0.0001"), decimal("0.0099")) } #[test] //#[ignore] fn test_add_borrow_integral() { assert_eq!(decimal("1.0") + decimal("-0.01"), decimal("0.99")) } #[test] //#[ignore] fn test_sub_borrow_integral() { assert_eq!(decimal("1.0") - decimal("0.01"), decimal("0.99")) } #[test] //#[ignore] fn test_add_borrow_integral_zeroes() { assert_eq!(decimal("1.0") + decimal("-0.99"), decimal("0.01")) } #[test] //#[ignore] fn test_sub_borrow_integral_zeroes() { assert_eq!(decimal("1.0") - decimal("0.99"), decimal("0.01")) } #[test] //#[ignore] fn test_borrow_from_negative() { assert_eq!(decimal("-1.0") + decimal("0.01"), decimal("-0.99")) } #[test] //#[ignore] fn test_add_into_fewer_digits() { assert_eq!(decimal("0.011") + decimal("-0.001"), decimal("0.01")) } // misc tests of arithmetic properties #[test] //#[ignore] fn test_sub_into_fewer_digits() { assert_eq!(decimal("0.011") - decimal("0.001"), decimal("0.01")) } #[test] //#[ignore] fn test_add_away_decimal() { assert_eq!(decimal("1.1") + decimal("-0.1"), decimal("1.0")) } #[test] //#[ignore] fn test_sub_away_decimal() { assert_eq!(decimal("1.1") - decimal("0.1"), decimal("1.0")) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::cmp::Ordering; use std::fmt; use std::ops::{Add, Mul, Sub}; #[macro_use] extern crate try_opt; extern crate num_bigint; use num_bigint::BigInt; extern crate num_traits; use num_traits::pow; /// Type implementing arbitrary-precision decimal arithmetic #[derive(Debug, Eq, Clone)] pub struct Decimal { digits: BigInt, decimal_index: usize, } impl Decimal { fn new(digits: BigInt, decimal_index: usize) -> Decimal { let mut value = Decimal { digits: digits, decimal_index: decimal_index, }; value.reduce(); value } pub fn try_from(mut input: &str) -> Option<Decimal> { // clear extraneous whitespace input = input.trim(); // don't bother to trim extraneous zeroes // leave it to users to manage their own memory // now build a representation of the number to parse let mut digits = String::with_capacity(input.len()); let mut decimal_index = None; for ch in input.chars() { match ch { '0'...'9' | '-' | '+' => { digits.push(ch); if let Some(idx) = decimal_index.as_mut() { *idx += 1; } } '.' => { if decimal_index.is_some() { return None; } decimal_index = Some(0) } _ => return None, } } Some(Decimal::new( try_opt!(digits.parse::<BigInt>().ok()), match decimal_index { Some(idx) => idx, None => 0, }, )) } /// Add precision to the less-precise value until precisions match /// /// Precision, in this case, is defined as the decimal index. fn equalize_precision(mut one: &mut Decimal, mut two: &mut Decimal) { fn expand(lower_precision: &mut Decimal, higher_precision: &Decimal) { let precision_difference = (higher_precision.decimal_index - lower_precision.decimal_index) as usize; lower_precision.digits = &lower_precision.digits * pow(BigInt::from(10_usize), precision_difference); lower_precision.decimal_index += precision_difference; } if one.decimal_index < two.decimal_index { expand(&mut one, &two) } else if one.decimal_index > two.decimal_index { expand(&mut two, &one) } assert_eq!(one.decimal_index, two.decimal_index); } /// Eliminate extraneous trailing zeroes /// /// This reduces the decimal index, so that the raw values are easier to parse fn reduce(&mut self) { let extra_zeroes = self.digits .to_string() // produce a decimal representation .chars() .rev() // trailing values .take(self.decimal_index) // not counting past the decimal point .take_while(|&c| c == '0') // counting only `0` digits .count(); self.digits = &self.digits / pow(BigInt::from(10_usize), extra_zeroes); self.decimal_index -= extra_zeroes; } } macro_rules! auto_impl_decimal_ops { ($trait:ident, $func_name:ident, $digits_operation:expr, $index_operation:expr) => { impl $trait for Decimal { type Output = Self; fn $func_name(mut self, mut rhs: Self) -> Self { Decimal::equalize_precision(&mut self, &mut rhs); Decimal::new( $digits_operation(self.digits, rhs.digits), $index_operation(self.decimal_index, rhs.decimal_index), ) } } }; } auto_impl_decimal_ops!(Add, add, |s, o| s + o, |s, _| s); auto_impl_decimal_ops!(Sub, sub, |s, o| s - o, |s, _| s); auto_impl_decimal_ops!(Mul, mul, |s, o| s * o, |s, o| s + o); macro_rules! auto_impl_decimal_cow { ($trait:ident, $func_name:ident, $digits_operation:expr, $return_type:ty) => { impl $trait for Decimal { fn $func_name(&self, other: &Self) -> $return_type { if self.decimal_index == other.decimal_index { $digits_operation(&self.digits, &other.digits) } else { // if we're here, the decimal indexes are unmatched. // We have to compare equal terms. // clone both sides so we can modify either of them as necessary. // not efficient, but whatever. let mut one = self.clone(); let mut two = other.clone(); Decimal::equalize_precision(&mut one, &mut two); one.$func_name(&two) } } } }; } auto_impl_decimal_cow!(PartialEq, eq, |a, b| a == b, bool); auto_impl_decimal_cow!(Ord, cmp, |a: &BigInt, b: &BigInt| a.cmp(b), Ordering); impl PartialOrd for Decimal { fn partial_cmp(&self, other: &Decimal) -> Option<Ordering> { Some(self.cmp(other)) } } impl fmt::Display for Decimal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // get a representation of the pure digits, // left-padded with zeroes let digits = format!("{:0>width$}", self.digits, width = self.decimal_index); if self.decimal_index == digits.len() { write!(f, "0.{}", digits) } else if self.decimal_index == 0 { write!(f, "{}", digits) } else { let (before_index, after_index) = digits.split_at(digits.len() - self.decimal_index); write!(f, "{}.{}", before_index, after_index) } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_display_temp() { for test_str in vec!["0", "1", "20", "0.3", "0.04", "50.05", "66.0006", "0.007"] { println!( "Decimal representation of \"{}\": {}", test_str, Decimal::try_from(test_str).expect("This should always become a decimal") ); assert_eq!( test_str, Decimal::try_from(test_str) .expect("This should always become a decimal") .to_string() ) } } } #}
填充/相关
Anagram
1. Readme
字谜
给出一个单词和可能的字谜列表,选择正确的子列表.
给出"listen"
和候选人名单一样"enlists" "google" "inlets" "banana"
程序应该返回一个包含"inlets"
的列表。
资源
受到 Extreme Startup 游戏的启发https://github.com/rchatley/extreme_startup
2. 开始你的表演
use std::collections::HashSet; pub fn anagrams_for<'a>(word: &str, possible_anagrams: &[&str]) -> HashSet<&'a str> { unimplemented!( "For the '{}' word find anagrams among the following words: {:?}", word, possible_anagrams ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // use std::collections::HashSet; use std::iter::FromIterator; fn process_anagram_case(word: &str, inputs: &[&str], expected: &[&str]) { let result = anagrams_for(word, inputs); let expected: HashSet<&str> = HashSet::from_iter(expected.iter().cloned()); assert_eq!(result, expected); } #[test] fn test_no_matches() { let word = "diaper"; let inputs = ["hello", "world", "zombies", "pants"]; let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_detect_simple_anagram() { let word = "ant"; let inputs = ["tan", "stand", "at"]; let outputs = vec!["tan"]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_does_not_confuse_different_duplicates() { let word = "galea"; let inputs = ["eagle"]; let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_eliminate_anagram_subsets() { let word = "good"; let inputs = ["dog", "goody"]; let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_detect_anagram() { let word = "listen"; let inputs = ["enlists", "google", "inlets", "banana"]; let outputs = vec!["inlets"]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_multiple_anagrams() { let word = "allergy"; let inputs = [ "gallery", "ballerina", "regally", "clergy", "largely", "leading", ]; let outputs = vec!["gallery", "regally", "largely"]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_case_insensitive_anagrams() { let word = "Orchestra"; let inputs = ["cashregister", "Carthorse", "radishes"]; let outputs = vec!["Carthorse"]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_unicode_anagrams() { let word = "ΑΒΓ"; // These words don't make sense, they're just greek letters cobbled together. let inputs = ["ΒΓΑ", "ΒΓΔ", "γβα"]; let outputs = vec!["ΒΓΑ", "γβα"]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_misleading_unicode_anagrams() { // Despite what a human might think these words different letters, the input uses Greek A and B // while the list of potential anagrams uses Latin A and B. let word = "ΑΒΓ"; let inputs = ["ABΓ"]; let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_does_not_detect_a_word_as_its_own_anagram() { let word = "banana"; let inputs = ["banana"]; let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_does_not_detect_a_differently_cased_word_as_its_own_anagram() { let word = "banana"; let inputs = ["bAnana"]; let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_does_not_detect_a_differently_cased_unicode_word_as_its_own_anagram() { let word = "ΑΒΓ"; let inputs = ["ΑΒγ"]; let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #[test] //#[ignore] fn test_same_bytes_different_chars() { let word = "a⬂"; // 61 E2 AC 82 let inputs = ["€a"]; // E2 82 AC 61 let outputs = vec![]; process_anagram_case(word, &inputs, &outputs); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashSet; fn sort(word: &String) -> String { let mut sorted: Vec<char> = word.chars().collect(); sorted.sort(); sorted.into_iter().collect() } pub fn anagrams_for<'a>(word: &str, inputs: &[&'a str]) -> HashSet<&'a str> { let lower = word.to_lowercase(); let sorted = sort(&lower); inputs .iter() .filter(|input| { let input_lower = input.to_lowercase(); lower != input_lower && sorted == sort(&input_lower) }) .cloned() .collect() } #}
填充/相关
Protein Translation
1. Readme
蛋白质转译
将 RNA 序列转译成蛋白质.
RNA 可以分解为三个称为密码子的核苷酸序列,然后转译成多肽,如下:
RNA:"AUGUUUUCU"
=>转译成
密码子:"AUG", "UUU", "UCU"
=>其成为具有以下序列的多肽=>
蛋白:"Methionine", "Phenylalanine", "Serine"
这有 64 个密码子,而这些密码子又相当于 20 个氨基酸;然而,在本练习中,所有密码子序列和所得氨基酸都不重要。如果它适用于一个密码子,该程序应该适用于所有这些密码子。但是,您可以随意扩展测试套件中的列表以包含它们.
还有三个终止密码子(也称为’STOP’密码子);如果遇到任何这些密码子(通过核糖体),那么所有转译结束,并终止蛋白质。
之后的所有后续密码子都会被忽略,如下所示:
RNA:"AUGUUUUCUUAAAUG"
=>
密码:"AUG", "UUU", "UCU", "UAA", "AUG"
=>
蛋白:"Methionine", "Phenylalanine", "Serine"
注意终止密码子"UAA"
终止转译,最终的蛋氨酸,不会转译成蛋白质序列。
以下是本练习所需的密码子和产生的氨基酸。
密码子 | 蛋白 |
---|---|
AUG | 蛋氨酸 |
UUU,UUC | 苯丙氨酸 |
UUA,UUG | 亮氨酸 |
UCU,UCC,UCA,UCG | 丝氨酸 |
UAU,UAC | 酪氨酸 |
UGU,UGC | 半胱氨酸 |
UGG | 色氨酸 |
UAA,UAG,UGA | STOP |
学习更多关于蛋白质转译:维基百科
资源
Tyler Long
2. 开始你的表演
use std::marker::PhantomData; pub struct CodonsInfo<'a> { // This field is here to make the template compile and not to // complain about unused type lifetime parameter "'a". Once you start // solving the exercise, delete this field and the 'std::marker::PhantomData' // import. phantom: PhantomData<&'a ()>, } impl<'a> CodonsInfo<'a> { pub fn name_for(&self, codon: &str) -> Option<&'a str> { unimplemented!( "Return the protein name for a '{}' codon or None, if codon string is invalid", codon ); } pub fn of_rna(&self, rna: &str) -> Option<Vec<&'a str>> { unimplemented!("Return a list of protein names that correspond to the '{}' RNA string or None if the RNA string is invalid", rna); } } pub fn parse<'a>(pairs: Vec<(&'a str, &'a str)>) -> CodonsInfo<'a> { unimplemented!( "Construct a new CodonsInfo struct from given pairs: {:?}", pairs ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_methionine() { let info = parse(make_pairs()); assert_eq!(info.name_for("AUG"), Some("methionine")); } #[test] //#[ignore] fn test_cysteine_tgt() { let info = parse(make_pairs()); assert_eq!(info.name_for("UGU"), Some("cysteine")); } #[test] //#[ignore] fn test_stop() { let info = parse(make_pairs()); assert_eq!(info.name_for("UAA"), Some("stop codon")); } #[test] //#[ignore] fn test_valine() { let info = parse(make_pairs()); assert_eq!(info.name_for("GUU"), Some("valine")); } #[test] //#[ignore] fn test_isoleucine() { let info = parse(make_pairs()); assert_eq!(info.name_for("AUU"), Some("isoleucine")); } #[test] //#[ignore] fn test_arginine_name() { let info = parse(make_pairs()); assert_eq!(info.name_for("CGA"), Some("arginine")); assert_eq!(info.name_for("AGA"), Some("arginine")); assert_eq!(info.name_for("AGG"), Some("arginine")); } #[test] //#[ignore] fn empty_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("").is_none()); } #[test] //#[ignore] fn x_is_not_shorthand_so_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("VWX").is_none()); } #[test] //#[ignore] fn too_short_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("AU").is_none()); } #[test] //#[ignore] fn too_long_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("ATTA").is_none()); } #[test] //#[ignore] fn test_translates_rna_strand_into_correct_protein() { let info = parse(make_pairs()); assert_eq!( info.of_rna("AUGUUUUGG"), Some(vec!["methionine", "phenylalanine", "tryptophan"]) ); } #[test] //#[ignore] fn test_stops_translation_if_stop_codon_present() { let info = parse(make_pairs()); assert_eq!( info.of_rna("AUGUUUUAA"), Some(vec!["methionine", "phenylalanine"]) ); } #[test] //#[ignore] fn test_stops_translation_of_longer_strand() { let info = parse(make_pairs()); assert_eq!( info.of_rna("UGGUGUUAUUAAUGGUUU"), Some(vec!["tryptophan", "cysteine", "tyrosine"]) ); } #[test] //#[ignore] fn test_invalid_codons() { let info = parse(make_pairs()); assert!(info.of_rna("CARROT").is_none()); } // The input data constructor. Returns a list of codon, name pairs. fn make_pairs() -> Vec<(&'static str, &'static str)> { let grouped = vec![ ("isoleucine", vec!["AUU", "AUC", "AUA"]), ("valine", vec!["GUU", "GUC", "GUA", "GUG"]), ("phenylalanine", vec!["UUU", "UUC"]), ("methionine", vec!["AUG"]), ("cysteine", vec!["UGU", "UGC"]), ("alanine", vec!["GCU", "GCC", "GCA", "GCG"]), ("glycine", vec!["GGU", "GGC", "GGA", "GGG"]), ("proline", vec!["CCU", "CCC", "CCA", "CCG"]), ("threonine", vec!["ACU", "ACC", "ACA", "ACG"]), ("serine", vec!["AGU", "AGC"]), ("tyrosine", vec!["UAU", "UAC"]), ("tryptophan", vec!["UGG"]), ("glutamine", vec!["CAA", "CAG"]), ("asparagine", vec!["AAU", "AAC"]), ("histidine", vec!["CAU", "CAC"]), ("glutamic acid", vec!["GAA", "GAG"]), ("aspartic acid", vec!["GAU", "GAC"]), ("lysine", vec!["AAA", "AAG"]), ("arginine", vec!["CGU", "CGC", "CGA", "CGG", "AGA", "AGG"]), ("stop codon", vec!["UAA", "UAG", "UGA"]), ]; let mut pairs = Vec::<(&'static str, &'static str)>::new(); for (name, codons) in grouped.into_iter() { for codon in codons { pairs.push((codon, name)); } } pairs.sort_by(|&(_, a), &(_, b)| a.cmp(b)); return pairs; } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; pub struct CodonInfo<'a> { actual_codons: HashMap<&'a str, &'a str>, } pub fn parse<'a>(pairs: Vec<(&'a str, &'a str)>) -> CodonInfo<'a> { CodonInfo { actual_codons: pairs.into_iter().collect(), } } impl<'a> CodonInfo<'a> { pub fn name_for(&self, codon: &str) -> Option<&'a str> { self.actual_codons.get(&codon).map(|&acid| acid) } pub fn of_rna(&self, strand: &str) -> Option<Vec<&'a str>> { strand .chars() .collect::<Vec<char>>() .chunks(3) .map(|chars| self.name_for(&chars.iter().collect::<String>())) .take_while(|result| result.is_none() || result.unwrap() != "stop codon") .collect() } } #}
填充/相关
Robot Name
1. Readme
机器人名称
管理机器人工厂设置.
当机器人从工厂车间出来时,他们没有名字。
第一次启动时,会生成一个随机名称,格式为两个大写字母后跟三个数字,如 RX837 或 BC811。
每隔一段时间我们就需要将机器人重置为出厂设置,这意味着他们的名字会被擦除。下次你问,它会以一个新的随机名称回复。
名称必须是随机的:它们不应遵循可预测的顺序。随机名称表示发生碰撞的风险。您的解决方案必须确保每个现有机器人都具有唯一名称。
资源
与 Paul Blackwell 在 gSchool 的调试会议.http://gschool.it
2. 开始你的表演
pub struct Robot; impl Robot { pub fn new() -> Self { unimplemented!("Construct a new Robot struct."); } pub fn name(&self) -> &str { unimplemented!("Return the reference to the robot's name."); } pub fn reset_name(&mut self) { unimplemented!("Assign a new unique name to the robot."); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn assert_name_matches_pattern(n: &str) { assert!(n.len() == 5, "name is exactly 5 characters long"); assert!( n[0..2].chars().all(|c| c >= 'A' && c <= 'Z'), "name starts with 2 uppercase letters" ); assert!( n[2..].chars().all(|c| c >= '0' && c <= '9'), "name ends with 3 numbers" ); } fn assert_name_is_persistent(r: &Robot) { // The type system already proves this, but why not. let n1 = r.name(); let n2 = r.name(); let n3 = r.name(); assert_eq!(n1, n2); assert_eq!(n2, n3); } #[test] fn test_name_should_match_expected_pattern() { let r = Robot::new(); assert_name_matches_pattern(r.name()); } #[test] //#[ignore] fn test_name_is_persistent() { assert_name_is_persistent(&Robot::new()); } #[test] //#[ignore] fn test_different_robots_have_different_names() { let r1 = Robot::new(); let r2 = Robot::new(); assert_ne!(r1.name(), r2.name(), "Robot names should be different"); } #[test] //#[ignore] fn test_new_name_should_match_expected_pattern() { let mut r = Robot::new(); assert_name_matches_pattern(r.name()); r.reset_name(); assert_name_matches_pattern(r.name()); } #[test] //#[ignore] fn test_new_name_is_persistent() { let mut r = Robot::new(); r.reset_name(); assert_name_is_persistent(&r); } #[test] //#[ignore] fn test_new_name_is_different_from_old_name() { let mut r = Robot::new(); let n1 = r.name().to_string(); r.reset_name(); let n2 = r.name().to_string(); assert_ne!(n1, n2, "Robot name should change when reset"); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { extern crate rand; use rand::{thread_rng, Rng}; pub struct Robot { name: String, } fn generate_name() -> String { let mut s = String::with_capacity(5); static LETTERS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static NUMBERS: &'static [u8] = b"0123456789"; for _ in 0..2 { s.push(thread_rng().choose(LETTERS).unwrap().clone() as char); } for _ in 0..3 { s.push(thread_rng().choose(NUMBERS).unwrap().clone() as char); } s } impl Robot { pub fn new() -> Robot { Robot { name: generate_name(), } } pub fn name<'a>(&'a self) -> &'a str { &self.name[..] } pub fn reset_name(&mut self) { self.name = generate_name(); } } #}
填充/相关
Book Store
1. Readme
书店
为了尝试鼓励,畅销 5 书系列书籍的销售,书店决定提供多书购买的折扣。
这五本书中的任何一本都要花 8 美元.
但是,如果您购买两本不同的书籍,那么这两本书将获得 5%的折扣。
如果您购买 3 本不同的书籍,您将获得 10%的折扣。
如果您购买 4 本不同的书籍,您将获得 20%的折扣。
如果您全部购买 5 件,即可获得 25%的折扣。
注意:如果您购买了 4 本书,但不同的书只有 3 本,那么您可以在 3 本书中获得 10%的折扣,但是第 4 本书的价格仍为 8 美元。
你的任务是写一段代码,来计算购物车(这 5 本书的任意搭配)的价格(只包含同一系列的书籍),给予尽可能大的折扣。
例如,购物车若是下面的书,那价格是多少?
- 第一本书的 2 份
- 第二本书的 2 份
- 第三本书的 2 份
- 第四本书的 1 份
- 第五本书的 1 份
将这 8 本书分组的一种方法是:
- 第 1 组 5 本书 -> 25%折扣(第 1,第 2,第 3,第 4,第 5)
- 还有 1 组 3 本书 -> 10%折扣(第 1 名,第 2 名,第 3 名)
这将总共给出:
- 5 本书,25%的折扣
- 还有 3 本书可享受 10%的折扣
导致:
- 5 x(8 - 2.00)== 5 x 6.00 == $ 30.00
- 还有 3 x(8 - 0.80)== 3 x 7.20 == 21.60 美元
总计 51.60 美元
但是,将这 8 本书分组的另一种方法是:
- 第 1 组 4 本书 -> 20%折扣(第 1,第 2,第 3,第 4)
- 还有 1 组 4 本书 -> 20%折扣(第 1,第 2,第 3,第 5)
这将总共给出:
- 4 本书,20%的折扣
- 还有 4 本书,20%的折扣
导致:
- 4 x(8 - 1.60)== 4 x 6.40 == 25.60 美元
- 还有 4 x(8 - 1.60)== 4 x 6.40 == 25.60 美元
总计 51.20 美元
51.20 美元是最大折扣的价格.
资源
灵感来自 Cyber-Dojo 的哈利波特卡塔.http://cyber-dojo.org
2. 开始你的表演
pub fn lowest_price(books: &[u32]) -> u32 { unimplemented!( "Find the lowest price of the bookbasket with books {:?}", books ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // Tests for book-store // // Generated by [script][script] using [canonical data][canonical-data] // // [script]: https://github.com/exercism/rust/blob/master/bin/init_exercise.py // [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/book-store/canonical_data.json /// Process a single test case for the property `total` /// /// All cases for the `total` property are implemented /// in terms of this function. /// /// Expected input format: ('basket', 'targetgrouping') fn process_total_case(input: (Vec<u32>, Vec<Vec<u32>>), expected: u32) { assert_eq!(lowest_price(&input.0), expected) } // Return the total basket price after applying the best discount. // Calculate lowest price for a shopping basket containing books only from // a single series. There is no discount advantage for having more than // one copy of any single book in a grouping. #[test] /// Only a single book fn test_only_a_single_book() { process_total_case((vec![1], vec![vec![1]]), 800); } #[test] //#[ignore] /// Two of the same book fn test_two_of_the_same_book() { process_total_case((vec![2, 2], vec![vec![2], vec![2]]), 1_600); } #[test] //#[ignore] /// Empty basket fn test_empty_basket() { process_total_case((vec![], vec![]), 0); } #[test] //#[ignore] /// Two different books fn test_two_different_books() { process_total_case((vec![1, 2], vec![vec![1, 2]]), 1_520); } #[test] //#[ignore] /// Three different books fn test_three_different_books() { process_total_case((vec![1, 2, 3], vec![vec![1, 2, 3]]), 2_160); } #[test] //#[ignore] /// Four different books fn test_four_different_books() { process_total_case((vec![1, 2, 3, 4], vec![vec![1, 2, 3, 4]]), 2_560); } #[test] //#[ignore] /// Five different books fn test_five_different_books() { process_total_case((vec![1, 2, 3, 4, 5], vec![vec![1, 2, 3, 4, 5]]), 3_000); } #[test] //#[ignore] /// Two groups of four is cheaper than group of five plus group of three fn test_two_groups_of_four_is_cheaper_than_group_of_five_plus_group_of_three() { process_total_case( ( vec![1, 1, 2, 2, 3, 3, 4, 5], vec![vec![1, 2, 3, 4], vec![1, 2, 3, 5]], ), 5_120, ); } #[test] //#[ignore] /// Group of four plus group of two is cheaper than two groups of three fn test_group_of_four_plus_group_of_two_is_cheaper_than_two_groups_of_three() { process_total_case( (vec![1, 1, 2, 2, 3, 4], vec![vec![1, 2, 3, 4], vec![1, 2]]), 4_080, ); } #[test] //#[ignore] /// Two each of first 4 books and 1 copy each of rest fn test_two_each_of_first_4_books_and_1_copy_each_of_rest() { process_total_case( ( vec![1, 1, 2, 2, 3, 3, 4, 4, 5], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4]], ), 5_560, ); } #[test] //#[ignore] /// Two copies of each book fn test_two_copies_of_each_book() { process_total_case( ( vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4, 5]], ), 6_000, ); } #[test] //#[ignore] /// Three copies of first book and 2 each of remaining fn test_three_copies_of_first_book_and_2_each_of_remaining() { process_total_case( ( vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4, 5], vec![1]], ), 6_800, ); } #[test] //#[ignore] /// Three each of first 2 books and 2 each of remaining books fn test_three_each_of_first_2_books_and_2_each_of_remaining_books() { process_total_case( ( vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4, 5], vec![1, 2]], ), 7_520, ); } #[test] //#[ignore] /// Four groups of four are cheaper than two groups each of five and three fn test_four_groups_of_four_are_cheaper_than_two_groups_each_of_five_and_three() { process_total_case( ( vec![1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5], vec![ vec![1, 2, 3, 4], vec![1, 2, 3, 5], vec![1, 2, 3, 4], vec![1, 2, 3, 5], ], ), 10_240, ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::cell::RefCell; use std::cmp::Ordering; use std::collections::hash_map::DefaultHasher; use std::collections::{BTreeSet, HashSet}; use std::hash::{Hash, Hasher}; use std::mem; type Book = u32; type GroupedBasket = Vec<Group>; type Price = u32; const BOOK_PRICE: Price = 800; #[derive(Debug, Clone, PartialEq, Eq)] struct Group(RefCell<BTreeSet<Book>>); impl Group { fn new() -> Group { Group(RefCell::new(BTreeSet::new())) } fn new_containing(book: Book) -> Group { let g = Group::new(); g.0.borrow_mut().insert(book); g } fn price(&self) -> Price { (self.0.borrow().len() as Price) * BOOK_PRICE * match self.0.borrow().len() { 2 => 95, 3 => 90, 4 => 80, 5 => 75, _ => 100, } / 100 } } impl Ord for Group { // we want to order groups first by qty contained DESC, then by lowest value ASC fn cmp(&self, other: &Group) -> Ordering { match other.0.borrow().len().cmp(&self.0.borrow().len()) { Ordering::Equal => { if self.0.borrow().len() == 0 { Ordering::Equal } else { self.0 .borrow() .iter() .next() .unwrap() .cmp(other.0.borrow().iter().next().unwrap()) } } otherwise => otherwise, } } } impl PartialOrd for Group { fn partial_cmp(&self, other: &Group) -> Option<Ordering> { Some(self.cmp(other)) } } impl Hash for Group { fn hash<H: Hasher>(&self, hasher: &mut H) { self.0.borrow().hash(hasher); } } fn basket_price(basket: &GroupedBasket) -> Price { basket.iter().map(|g| g.price()).sum() } /// Compute the hash of a GroupedBasket /// /// Note that we don't actually care at all about the _values_ within /// the groups, only their lengths. Therefore, let's hash not the actual /// GB but its lengths. fn hash_of(basket: &GroupedBasket) -> u64 { let lengths = basket .iter() .map(|g| g.0.borrow().len()) .collect::<Vec<_>>(); let mut hasher = DefaultHasher::new(); lengths.hash(&mut hasher); hasher.finish() } pub fn lowest_price(books: &[Book]) -> Price { DecomposeGroups::new(books) .map(|gb| basket_price(&gb)) .min() .unwrap_or(0) } struct DecomposeGroups { prev_states: HashSet<u64>, next: Option<GroupedBasket>, } impl Iterator for DecomposeGroups { type Item = GroupedBasket; fn next(&mut self) -> Option<Self::Item> { // our goal here: produce a stream of valid groups, differentiated by their // counts, from most compact to most dispersed. // // Algorithm: // - Start with the most compact groups possible // - If the number of groups == 0 or the max population of any group == 1, return None // - For every item in the most populous group: // - Try removing it and adding it to a smaller group. // - Can any smaller group accept it? if yes, move it there and return // - If it cannot be added to any smaller group, try the next item from this set // - If no item from the most populous group can be added to any smaller group, // then move the last item from the most populous group into a new group, alone, // and return let return_value = self.next.clone(); if let Some(groups) = mem::replace(&mut self.next, None) { if !(groups.is_empty() || groups.iter().all(|g| g.0.borrow().len() == 1)) { let mut hypothetical; for mpg_book in groups[0].0.borrow().iter() { for (idx, other_group) in groups[1..].iter().enumerate() { if !other_group.0.borrow().contains(mpg_book) { hypothetical = groups.clone(); hypothetical[0].0.borrow_mut().remove(mpg_book); hypothetical[1 + idx].0.borrow_mut().insert(*mpg_book); hypothetical.sort(); let hypothetical_hash = hash_of(&hypothetical); if !self.prev_states.contains(&hypothetical_hash) { self.prev_states.insert(hypothetical_hash); mem::replace(&mut self.next, Some(hypothetical)); return return_value; } } } } // we've gone through all the items of the most populous group, // and none of them can be added to any other existing group. // We need to create a new group; let book = { let backing_bt = groups[0].0.borrow(); let mut book_iter = backing_bt.iter(); book_iter.next().unwrap().clone() }; hypothetical = groups.clone(); hypothetical[0].0.borrow_mut().remove(&book); hypothetical.push(Group::new_containing(book)); hypothetical.sort(); self.prev_states.insert(hash_of(&hypothetical)); mem::replace(&mut self.next, Some(hypothetical)); } } return_value } } impl DecomposeGroups { fn new(books: &[Book]) -> DecomposeGroups { let mut book_groups = GroupedBasket::new(); 'nextbook: for book in books { for idx in 0..book_groups.len() { if !book_groups[idx].0.borrow().contains(&book) { book_groups[idx].0.borrow_mut().insert(*book); continue 'nextbook; } } // if we're here, we still haven't found a place for the book. // better add it to a new group book_groups.push(Group::new_containing(*book)); } book_groups.sort(); DecomposeGroups { next: Some(book_groups), prev_states: HashSet::new(), } } } #}
填充/相关
难
- Book Store
- OCR Numbers
- Minesweeper
- Dominoes
- Parallel Letter Frequency
- Rectangles
- Forth
- Circular Buffer
- React
OCR Numbers
1. Readme
OCR 号码
给定一个 3 x 4 ,由竖线符号,下划线和空格组成的网格,确定代表哪个数字,或者是否是乱码。
第一步
首先,将简单的二进制字体,转换为包含 0 或 1 的字符串。
二进制字体使用竖线符号和下划线,四行高,三列宽.
_ #
| | # zero.
|_| #
# 第 4 行总空着
转换为”0”
#
| # one.
| #
# (4行空)
转换为”1”
如果输入的大小正确,但无法识别,则程序应返回”?”
如果输入的大小不正确,程序应该返回错误。
第二步
更新您的程序以识别多二进制字符串,用 ? 替换乱码。
第三步
更新程序,以识别所有数字 0 到 9,既可以单独识别,也可以作为更大字符串的一部分识别。
_
_|
|_
转换为”2”
_ _ _ _ _ _ _ _ #
| _| _||_||_ |_ ||_||_|| | # 十进制数.
||_ _| | _||_| ||_| _||_| #
# 第 4 行空着
被转换为”1234567890”
第四步
更新程序以处理多个数字,每 4 行。转换多行时,请使用逗号连接行.
_ _
| _| _|
||_ _|
_ _
|_||_ |_
| _||_|
_ _ _
||_||_|
||_| _|
被转换为”123,456,789”
资源
灵感来自银行 OCR katahttp://codingdojo.org/cgi-bin/wiki.pl?KataBankOCR
2. 开始你的表演
// The code below is a stub. Just enough to satisfy the compiler. // In order to pass the tests you can add-to or change any of this code. #[derive(Debug, PartialEq)] pub enum Error { InvalidRowCount(usize), InvalidColumnCount(usize), } pub fn convert(input: &str) -> Result<String, Error> { unimplemented!("Convert the input '{}' to a string", input); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn input_with_lines_not_multiple_of_four_is_error() { let input = " _ \n".to_string() + "| |\n" + " "; assert_eq!(Err(Error::InvalidRowCount(3)), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn input_with_columns_not_multiple_of_three_is_error() { let input = " \n".to_string() + " |\n" + " |\n" + " "; assert_eq!(Err(Error::InvalidColumnCount(4)), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn unrecognized_characters_return_question_mark() { let input = " \n".to_string() + " _\n" + " |\n" + " "; assert_eq!(Ok("?".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_0() { let input = " _ \n".to_string() + "| |\n" + "|_|\n" + " "; assert_eq!(Ok("0".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_1() { let input = " \n".to_string() + " |\n" + " |\n" + " "; assert_eq!(Ok("1".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_2() { let input = " _ \n".to_string() + " _|\n" + "|_ \n" + " "; assert_eq!(Ok("2".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_3() { let input = " _ \n".to_string() + " _|\n" + " _|\n" + " "; assert_eq!(Ok("3".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_4() { let input = " \n".to_string() + "|_|\n" + " |\n" + " "; assert_eq!(Ok("4".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_5() { let input = " _ \n".to_string() + "|_ \n" + " _|\n" + " "; assert_eq!(Ok("5".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_6() { let input = " _ \n".to_string() + "|_ \n" + "|_|\n" + " "; assert_eq!(Ok("6".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_7() { let input = " _ \n".to_string() + " |\n" + " |\n" + " "; assert_eq!(Ok("7".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_8() { let input = " _ \n".to_string() + "|_|\n" + "|_|\n" + " "; assert_eq!(Ok("8".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_9() { let input = " _ \n".to_string() + "|_|\n" + " _|\n" + " "; assert_eq!(Ok("9".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_110101100() { let input = " _ _ _ _ \n".to_string() + " | || | || | | || || |\n" + " | ||_| ||_| | ||_||_|\n" + " "; assert_eq!(Ok("110101100".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn replaces_only_garbled_numbers_with_question_mark() { let input = " _ _ _ \n".to_string() + " | || | || | || || |\n" + " | | _| ||_| | ||_||_|\n" + " "; assert_eq!(Ok("11?10?1?0".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn recognizes_string_of_decimal_numbers() { let input = " _ _ _ _ _ _ _ _ \n".to_string() + " | _| _||_||_ |_ ||_||_|| |\n" + " ||_ _| | _||_| ||_| _||_|\n" + " "; assert_eq!(Ok("1234567890".to_string()), convert(&input)); } #[test] //#[ignore] #[cfg_attr(rustfmt, rustfmt_skip)] fn numbers_across_multiple_lines_are_joined_by_commas() { let input = " _ _ \n".to_string() + " | _| _|\n" + " ||_ _|\n" + " \n" + " _ _ \n" + "|_||_ |_ \n" + " | _||_|\n" + " \n" + " _ _ _ \n" + " ||_||_|\n" + " ||_| _|\n" + " "; assert_eq!(Ok("123,456,789".to_string()), convert(&input)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::BTreeMap; #[derive(Debug, PartialEq)] pub enum Error { InvalidRowCount(usize), InvalidColumnCount(usize), } pub fn convert(input: &str) -> Result<String, Error> { let line_count = input.lines().count(); if line_count % 4 != 0 { return Err(Error::InvalidRowCount(line_count)); } for line in input.lines() { let char_count = line.chars().count(); if char_count % 3 != 0 { return Err(Error::InvalidColumnCount(char_count)); } } let y = input.lines().collect::<Vec<_>>(); let mut converted_lines = vec![]; for char_lines in y.chunks(4) { let mut unparsed_characters = BTreeMap::new(); for line in char_lines { let line_chars = line.chars().collect::<Vec<_>>(); for (char_number, char_chunk) in line_chars.chunks(3).enumerate() { let char_chars = unparsed_characters.entry(char_number).or_insert(vec![]); for c in char_chunk { char_chars.push(*c); } } } let mut parsed_characters = String::new(); for (_, v) in unparsed_characters { parsed_characters.push(convert_character(&v)); } converted_lines.push(parsed_characters.to_string()); } Ok(converted_lines.join(",")) } fn convert_character(input: &Vec<char>) -> char { if &input[..] == [' ', '_', ' ', '|', ' ', '|', '|', '_', '|', ' ', ' ', ' '] { '0' } else if &input[..] == [' ', ' ', ' ', ' ', ' ', '|', ' ', ' ', '|', ' ', ' ', ' '] { '1' } else if &input[..] == [' ', '_', ' ', ' ', '_', '|', '|', '_', ' ', ' ', ' ', ' '] { '2' } else if &input[..] == [' ', '_', ' ', ' ', '_', '|', ' ', '_', '|', ' ', ' ', ' '] { '3' } else if &input[..] == [' ', ' ', ' ', '|', '_', '|', ' ', ' ', '|', ' ', ' ', ' '] { '4' } else if &input[..] == [' ', '_', ' ', '|', '_', ' ', ' ', '_', '|', ' ', ' ', ' '] { '5' } else if &input[..] == [' ', '_', ' ', '|', '_', ' ', '|', '_', '|', ' ', ' ', ' '] { '6' } else if &input[..] == [' ', '_', ' ', ' ', ' ', '|', ' ', ' ', '|', ' ', ' ', ' '] { '7' } else if &input[..] == [' ', '_', ' ', '|', '_', '|', '|', '_', '|', ' ', ' ', ' '] { '8' } else if &input[..] == [' ', '_', ' ', '|', '_', '|', ' ', '_', '|', ' ', ' ', ' '] { '9' } else { '?' } } #}
填充/相关
Minesweeper
1. Readme
扫雷
将数字添加到扫雷板上.
扫雷器是一个流行的游戏,其中用户要用数字提示,来找到地雷,这些数字提示指示有多少地雷直接相邻(水平,垂直,对角,总 9 个格),围成一个正方形.
在这个练习中,您必须创建一些代码,这些代码计算与正方形相邻的地雷数量,并且像这样转换地雷板(其中*
表示地雷):
+-----+
| * * |
| * |
| * |
| |
+-----+
变成:
+-----+
|1*3*1|
|13*31|
| 2*2 |
| 111 |
+-----+
2. 开始你的表演
pub fn annotate(minefield: &[&str]) -> Vec<String> { unimplemented!("\nAnnotate each square of the given minefield with the number of mines that surround said square (blank if there are no surrounding mines):\n{:#?}\n", minefield); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn remove_annotations(board: &[&str]) -> Vec<String> { board.iter().map(|r| remove_annotations_in_row(r)).collect() } fn remove_annotations_in_row(row: &str) -> String { row.chars() .map(|ch| match ch { '*' => '*', _ => ' ', }) .collect() } fn run_test(test_case: &[&str]) { let cleaned = remove_annotations(test_case); let cleaned_strs = cleaned.iter().map(|r| &r[..]).collect::<Vec<_>>(); let expected = test_case.iter().map(|&r| r.to_string()).collect::<Vec<_>>(); assert_eq!(expected, annotate(&cleaned_strs)); } #[test] fn no_rows() { run_test(&[]); } #[test] //#[ignore] fn no_columns() { run_test(&[""]); } #[test] //#[ignore] fn no_mines() { run_test(&[" ", " ", " "]); } #[test] //#[ignore] fn board_with_only_mines() { run_test(&["***", "***", "***"]); } #[test] //#[ignore] fn mine_surrounded_by_spaces() { run_test(&["111", "1*1", "111"]); } #[test] //#[ignore] fn space_surrounded_by_mines() { run_test(&["***", "*8*", "***"]); } #[test] //#[ignore] fn horizontal_line() { run_test(&["1*2*1"]); } #[test] //#[ignore] fn horizontal_line_mines_at_edges() { run_test(&["*1 1*"]); } #[test] //#[ignore] fn vertical_line() { run_test(&["1", "*", "2", "*", "1"]); } #[test] //#[ignore] fn vertical_line_mines_at_edges() { run_test(&["*", "1", " ", "1", "*"]); } #[test] //#[ignore] fn cross() { run_test(&[" 2*2 ", "25*52", "*****", "25*52", " 2*2 "]); } #[test] //#[ignore] fn large_board() { run_test(&["1*22*1", "12*322", " 123*2", "112*4*", "1*22*2", "111111"]); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { struct Board { pieces: Vec<Vec<char>>, num_rows: usize, num_cols: usize } impl Board { fn annotated(&self) -> Vec<String> { (0..self.num_rows).map(|y| self.annotated_row(y)).collect() } fn annotated_row(&self, y: usize) -> String { self.pieces[y] .iter() .enumerate() .map(|(x,&c)| if c == ' ' {self.count_neighbouring_mines_char(x, y)} else {c}) .collect::<String>() } fn count_neighbouring_mines_char(&self, x: usize, y: usize) -> char { let mut count = 0; for x1 in neighbouring_points(x, self.num_cols) { for y1 in neighbouring_points(y, self.num_rows) { let piece = self.pieces[y1][x1]; if piece == '*' { count += 1; } } } if count == 0 { ' ' } else { (('0' as u8) + count) as char } } } pub fn annotate(pieces: &[&str]) -> Vec<String> { if pieces.len() == 0 { return Vec::new(); } let pieces_vec = pieces.iter().map(|&r| r.chars().collect()).collect(); Board {pieces: pieces_vec, num_rows: pieces.len(), num_cols: pieces[0].len()}.annotated() } fn neighbouring_points(x: usize, limit: usize) -> Vec<usize> { let mut offsets = vec![x]; if x >= 1 { offsets.push(x-1); } if x+2 <= limit { offsets.push(x+1); } offsets } #}
填充/相关
Dominoes
1. Readme
多米诺骨牌
制作一个多米诺骨牌.
计算给出的多米诺骨牌的排序方法,使它们形成一个正确的多米诺骨牌链(石头的一半上的数值,与相邻石头的一半上的数值相匹配),并且(第一块石头和最后一块石头)石头的一半上的数值没有邻居,且它们的数值匹配.
比如,给出石头[2|1]
,[2|3]
和[1|3]
你应该计算一些类似[1|2] [2|3] [3|1]
或[3|2] [2|1] [1|3]
或[1|3] [3|2] [2|1]
的东西等,其中第一个和最后一个数字是相同的。
对于以下石头[1|2]
,[4|1]
和[2|3]
,它的结果骨牌链无效:[4|1] [1|2] [2|3]
第一个和最后一个数字不一样。4!= 3
一些测试用例可以在一个骨牌链解决方案中,使用重复的石头,假设使用了多个骨牌集合。
2. 开始你的表演
pub fn chain(input: &[(u8, u8)]) -> Option<Vec<(u8, u8)>> { unimplemented!("From the given input '{:?}' construct a proper dominoes chain or return None if it is not possible.", input); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // type Domino = (u8, u8); #[derive(Debug)] enum CheckResult { GotInvalid, // chain returned None Correct, ChainingFailure(Vec<Domino>), // failure to match the dots at the right side of one domino with // the one on the left side of the next LengthMismatch(Vec<Domino>), DominoMismatch(Vec<Domino>), // different dominoes are used in input and output } use CheckResult::*; fn normalize(d: &Domino) -> Domino { match d { &(m, n) if m > n => (n, m), &(m, n) => (m, n), } } fn check(input: &[Domino]) -> CheckResult { let output = match chain(input) { None => return GotInvalid, Some(o) => o, }; if input.len() != output.len() { return LengthMismatch(output); } else if input.len() == 0 { // and thus output.len() == 0 return Correct; } let mut output_sorted = output.iter().map(|d| normalize(d)).collect::<Vec<Domino>>(); output_sorted.sort(); let mut input_sorted = input.iter().map(|d| normalize(d)).collect::<Vec<Domino>>(); input_sorted.sort(); if input_sorted != output_sorted { return DominoMismatch(output); } // both input and output have at least 1 element // This essentially puts the first element after the last one, thereby making it // easy to check whether the domino chains "wraps around". let mut fail = false; { let mut n = output[0].1; let iter = output.iter().skip(1).chain(output.iter().take(1)); for &(first, second) in iter { if n != first { fail = true; break; } n = second } } if fail { ChainingFailure(output) } else { Correct } } fn assert_correct(input: &[Domino]) { match check(&input) { Correct => (), GotInvalid => panic!("Unexpectedly got invalid on input {:?}", input), ChainingFailure(output) => panic!( "Chaining failure for input {:?}, output {:?}", input, output ), LengthMismatch(output) => { panic!("Length mismatch for input {:?}, output {:?}", input, output) } DominoMismatch(output) => { panic!("Domino mismatch for input {:?}, output {:?}", input, output) } } } #[test] fn empty_input_empty_output() { let input = &[]; assert_eq!(chain(input), Some(vec![])); } #[test] //#[ignore] fn singleton_input_singleton_output() { let input = &[(1, 1)]; assert_correct(input); } #[test] //#[ignore] fn singleton_that_cant_be_chained() { let input = &[(1, 2)]; assert_eq!(chain(input), None); } #[test] //#[ignore] fn no_repeat_numbers() { let input = &[(1, 2), (3, 1), (2, 3)]; assert_correct(input); } #[test] //#[ignore] fn can_reverse_dominoes() { let input = &[(1, 2), (1, 3), (2, 3)]; assert_correct(input); } #[test] //#[ignore] fn no_chains() { let input = &[(1, 2), (4, 1), (2, 3)]; assert_eq!(chain(input), None); } #[test] //#[ignore] fn disconnected_simple() { let input = &[(1, 1), (2, 2)]; assert_eq!(chain(input), None); } #[test] //#[ignore] fn disconnected_double_loop() { let input = &[(1, 2), (2, 1), (3, 4), (4, 3)]; assert_eq!(chain(input), None); } #[test] //#[ignore] fn disconnected_single_isolated() { let input = &[(1, 2), (2, 3), (3, 1), (4, 4)]; assert_eq!(chain(input), None); } #[test] //#[ignore] fn need_backtrack() { let input = &[(1, 2), (2, 3), (3, 1), (2, 4), (2, 4)]; assert_correct(input); } #[test] //#[ignore] fn separate_loops() { let input = &[(1, 2), (2, 3), (3, 1), (1, 1), (2, 2), (3, 3)]; assert_correct(input); } #[test] //#[ignore] fn nine_elements() { let input = &[ (1, 2), (5, 3), (3, 1), (1, 2), (2, 4), (1, 6), (2, 3), (3, 4), (5, 6), ]; assert_correct(input); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::iter; pub type Domino = (u8, u8); /// A table keeping track of available dominoes. /// /// Effectively a 6x6 matrix. Each position denotes whether a domino is available with that column /// dots and row dots. Positions are mirrored ((3,4) == (4,3)), except for positions with equal row /// and column numbers. struct AvailabilityTable { m: Vec<u8>, } impl AvailabilityTable { fn new() -> AvailabilityTable { AvailabilityTable { m: iter::repeat(0).take(6 * 6).collect(), } } fn get(&self, x: u8, y: u8) -> u8 { self.m[((x - 1) * 6 + (y - 1)) as usize] } fn set(&mut self, x: u8, y: u8, v: u8) { let m = &mut self.m[..]; m[((x - 1) * 6 + (y - 1)) as usize] = v; } fn add(&mut self, x: u8, y: u8) { if x == y { let n = self.get(x, y); self.set(x, y, n + 1) // Along the diagonal } else { let m = self.get(x, y); self.set(x, y, m + 1); let n = self.get(y, x); self.set(y, x, n + 1); } } fn remove(&mut self, x: u8, y: u8) { if self.get(x, y) > 0 { if x == y { let n = self.get(x, y); self.set(x, y, n - 1) // Along the diagonal } else { let m = self.get(x, y); self.set(x, y, m - 1); let n = self.get(y, x); self.set(y, x, n - 1); } } else { // For this toy code hard explicit fail is best panic!("remove for 0 stones: ({:?}, {:?})", x, y) } } fn pop_first(&mut self, x: u8) -> Option<u8> { for y in 1..7 { if self.get(x, y) > 0 { self.remove(x, y); return Some(y); } } None } } pub fn chain(dominoes: &[Domino]) -> Option<Vec<Domino>> { match dominoes.len() { 0 => Some(vec![]), 1 => if dominoes[0].0 == dominoes[0].1 { Some(vec![dominoes[0]]) } else { None }, _ => { // First check if the total number of each amount of dots is even, if not it's not // possible to complete a cycle. This follows from that it's an Eulerian path. let mut v: Vec<u8> = vec![0, 0, 0, 0, 0, 0]; // Keep the mutable borrow in a small scope here to allow v.iter(). { let vs = &mut v[..]; for dom in dominoes.iter() { vs[dom.0 as usize - 1] += 1; vs[dom.1 as usize - 1] += 1; } } for n in v.iter() { if n % 2 != 0 { return None; } } let chain = chain_worker(dominoes); if chain.len() == dominoes.len() { Some(chain) } else { None } } } } fn chain_worker(dominoes: &[Domino]) -> Vec<Domino> { let mut doms = dominoes.to_vec(); let first = doms.pop().unwrap(); let mut t = AvailabilityTable::new(); for dom in doms.iter() { t.add(dom.0, dom.1) } let mut v: Vec<Domino> = Vec::new(); v.push(first); let mut n = first.1; // Number to connect to while let Some(m) = t.pop_first(n) { v.push((n, m)); n = m; } v } #}
填充/相关
Parallel Letter Frequency
1. Readme
并发字母频率
使用并行计算,文本中的字母频率。
并行性是并行的,也可以按顺序进行。一个常见的例子是计算字母的频率。创建一个函数,返回文本列表中每个字母的总频率,并使用并行性。
Rust 中的并发性字母频率
在这里了解更多关于 Rust 的并发性:
加分
这个练习还包括一个基准,以一个顺序实现为基线。您可以将您的解决方案与基准进行比较。观察不同大小的输入,对每个性能的影响。可以使用并行编程技术。来超越基准吗?
在本文中,test::Bencher
是不稳定的,只能在nightlyRust 可用。用 Cargo 运行基准:
cargo bench
如果你使用 rustup.rs:
rustup run nightly cargo bench
了解 nightly Rust 的更多信息:
2. 开始你的表演
use std::collections::HashMap; pub fn frequency(input: &[&str], worker_count: usize) -> HashMap<char, usize> { unimplemented!( "Count the frequency of letters in the given input '{:?}'. Ensure that you are using {} to process the input.", input, match worker_count { 1 => format!("1 worker"), _ => format!("{} workers", worker_count), } ); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // use std::collections::HashMap; // Poem by Friedrich Schiller. The corresponding music is the European Anthem. const ODE_AN_DIE_FREUDE: [&'static str; 8] = [ "Freude schöner Götterfunken", "Tochter aus Elysium,", "Wir betreten feuertrunken,", "Himmlische, dein Heiligtum!", "Deine Zauber binden wieder", "Was die Mode streng geteilt;", "Alle Menschen werden Brüder,", "Wo dein sanfter Flügel weilt.", ]; // Dutch national anthem const WILHELMUS: [&'static str; 8] = [ "Wilhelmus van Nassouwe", "ben ik, van Duitsen bloed,", "den vaderland getrouwe", "blijf ik tot in den dood.", "Een Prinse van Oranje", "ben ik, vrij, onverveerd,", "den Koning van Hispanje", "heb ik altijd geëerd.", ]; // American national anthem const STAR_SPANGLED_BANNER: [&'static str; 8] = [ "O say can you see by the dawn's early light,", "What so proudly we hailed at the twilight's last gleaming,", "Whose broad stripes and bright stars through the perilous fight,", "O'er the ramparts we watched, were so gallantly streaming?", "And the rockets' red glare, the bombs bursting in air,", "Gave proof through the night that our flag was still there;", "O say does that star-spangled banner yet wave,", "O'er the land of the free and the home of the brave?", ]; #[test] fn test_no_texts() { assert_eq!(frequency(&[], 4), HashMap::new()); } #[test] //#[ignore] fn test_one_letter() { let mut hm = HashMap::new(); hm.insert('a', 1); assert_eq!(frequency(&["a"], 4), hm); } #[test] //#[ignore] fn test_case_insensitivity() { let mut hm = HashMap::new(); hm.insert('a', 2); assert_eq!(frequency(&["aA"], 4), hm); } #[test] //#[ignore] fn test_many_empty_lines() { let mut v = Vec::with_capacity(1000); for _ in 0..1000 { v.push(""); } assert_eq!(frequency(&v[..], 4), HashMap::new()); } #[test] //#[ignore] fn test_many_times_same_text() { let mut v = Vec::with_capacity(1000); for _ in 0..1000 { v.push("abc"); } let mut hm = HashMap::new(); hm.insert('a', 1000); hm.insert('b', 1000); hm.insert('c', 1000); assert_eq!(frequency(&v[..], 4), hm); } #[test] //#[ignore] fn test_punctuation_doesnt_count() { assert!(!frequency(&WILHELMUS, 4).contains_key(&',')); } #[test] //#[ignore] fn test_numbers_dont_count() { assert!(!frequency(&["Testing, 1, 2, 3"], 4).contains_key(&'1')); } #[test] //#[ignore] fn test_all_three_anthems_1_worker() { let mut v = Vec::new(); for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() { for line in anthem.iter() { v.push(*line); } } let freqs = frequency(&v[..], 1); assert_eq!(freqs.get(&'a'), Some(&49)); assert_eq!(freqs.get(&'t'), Some(&56)); assert_eq!(freqs.get(&'ü'), Some(&2)); } #[test] //#[ignore] fn test_all_three_anthems_3_workers() { let mut v = Vec::new(); for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() { for line in anthem.iter() { v.push(*line); } } let freqs = frequency(&v[..], 3); assert_eq!(freqs.get(&'a'), Some(&49)); assert_eq!(freqs.get(&'t'), Some(&56)); assert_eq!(freqs.get(&'ü'), Some(&2)); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; use std::sync::mpsc::channel; use std::thread; /// Compute the frequency of each letter (technically of each unicode codepoint) using the given /// number of worker threads. pub fn frequency(texts: &[&str], num_workers: usize) -> HashMap<char, usize> { assert!(num_workers > 0); let part_size_floor = texts.len() / num_workers; let rem = texts.len() % num_workers; let part_size = if rem > 0 { part_size_floor + 1 } else { part_size_floor }; let mut parts: Vec<Vec<String>> = Vec::with_capacity(part_size); for _ in 0..num_workers { parts.push(Vec::with_capacity(part_size)); } let mut i = 0; for line in texts.iter() { // We'll need to clone those strings in order to satisfy some lifetime guarantees. Basically // it's hard for the system to be sure that the threads spawned don't outlive the strings. parts[i].push(line.to_string()); i = (i + 1) % num_workers; } let (tx, rx) = channel(); for part in parts { let tx = tx.clone(); thread::spawn(move || { tx.send(count(part)).unwrap(); }); } let mut results: HashMap<char, usize> = HashMap::new(); for _ in 0..num_workers { let part_results = rx.recv().unwrap(); for (c, n) in part_results.into_iter() { *results.entry(c).or_insert(0) += n; } } results } fn count(lines: Vec<String>) -> HashMap<char, usize> { let mut results: HashMap<char, usize> = HashMap::new(); for line in lines.iter() { for c in line.chars() { if c.is_alphabetic() { *results.entry(c.to_lowercase().next().unwrap()).or_insert(0) += 1; } } } results } #}
填充/相关
Rectangles
1. Readme
矩形
计算 ASCII 图中的矩形的数目,如下所示.
+--+
++ |
+-++--+
| | |
+--+--+
上面的图表包含 6 个矩形:
+-----+
| |
+-----+
+--+
| |
| |
| |
+--+
+--+
| |
+--+
+--+
| |
+--+
+--+
| |
+--+
++
++
你可以假设输入总是一个适当的矩形(即每行的长度,等于第一行的长度)。
2. 开始你的表演
pub fn count(lines: &[&str]) -> u32 { unimplemented!("\nDetermine the count of rectangles in the ASCII diagram represented by the following lines:\n{:#?}\n.", lines); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_zero_area_1() { let lines = &[]; assert_eq!(0, count(lines)) } #[test] //#[ignore] fn test_zero_area_2() { let lines = &[""]; assert_eq!(0, count(lines)) } #[test] //#[ignore] fn test_empty_area() { let lines = &[" "]; assert_eq!(0, count(lines)) } #[test] //#[ignore] fn test_one_rectangle() { let lines = &["+-+", "| |", "+-+"]; assert_eq!(1, count(lines)) } #[test] //#[ignore] fn test_two_rectangles_no_shared_parts() { let lines = &[" +-+", " | |", "+-+-+", "| | ", "+-+ "]; assert_eq!(2, count(lines)) } #[test] //#[ignore] fn test_five_rectangles_three_regions() { let lines = &[" +-+", " | |", "+-+-+", "| | |", "+-+-+"]; assert_eq!(5, count(lines)) } #[test] //#[ignore] fn rectangle_of_height_1() { let lines = &["+--+", "+--+"]; assert_eq!(1, count(lines)) } #[test] //#[ignore] fn rectangle_of_width_1() { let lines = &["++", "||", "++"]; assert_eq!(1, count(lines)) } #[test] //#[ignore] fn unit_square() { let lines = &["++", "++"]; assert_eq!(1, count(lines)) } #[test] //#[ignore] fn test_incomplete_rectangles() { let lines = &[" +-+", " |", "+-+-+", "| | -", "+-+-+"]; assert_eq!(1, count(lines)) } #[test] //#[ignore] fn test_complicated() { let lines = &[ "+------+----+", "| | |", "+---+--+ |", "| | |", "+---+-------+", ]; assert_eq!(3, count(lines)) } #[test] //#[ignore] fn test_not_so_complicated() { let lines = &[ "+------+----+", "| | |", "+------+ |", "| | |", "+---+-------+", ]; assert_eq!(2, count(lines)) } #[test] //#[ignore] fn test_large_input_with_many_rectangles() { let lines = &[ "+---+--+----+", "| +--+----+", "+---+--+ |", "| +--+----+", "+---+--+--+-+", "+---+--+--+-+", "+------+ | |", " +-+", ]; assert_eq!(60, count(lines)) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::{HashMap, HashSet}; // We first look for corners that are connected by a straight uninterrupted line. // At the same time we make a note for each corner in which direction it's connected. // // Later we simply scan over the corners, pairing each top-left with each right-bottom corner // (provided it's to the right and below the top-left corner) and look if all four lines // of the rectangle exist. // // To simplify scanning for lines and points we run the horizontal lines algorithm on a // seemingly transposed version of the input and later transpose the results again. // Note: values of these constants are used in transposition, they're not random. const CONN_LEFT: u8 = 0x1; const CONN_RIGHT: u8 = 0x2; const CONN_UP: u8 = 0x4; const CONN_DOWN: u8 = 0x8; #[derive(PartialEq, Eq, Hash)] struct Point { x: usize, y: usize } #[derive(PartialEq, Eq, Hash)] struct Line { x1: usize, y1: usize, x2: usize, y2: usize } #[derive(PartialEq, Eq, Clone, Copy)] enum Symbol { Corner, // '+' Connect, // '|' or '-' depending on direction Other // ' ', or anything really } // The input area. struct RealArea { width: usize, height: usize, chars: Vec<Vec<char>> } trait Area { fn width(&self) -> usize; fn height(&self) -> usize; fn symbol_at(&self, x: usize, y: usize) -> Symbol; } // For horizontal scanning impl Area for RealArea { fn width(&self) -> usize { self.width } fn height(&self) -> usize { self.height } fn symbol_at(&self, x: usize, y: usize) -> Symbol { match self.chars[y][x] { '+' => Symbol::Corner, '-' => Symbol::Connect, _ => Symbol::Other } } } struct TransposedArea<'a>(&'a RealArea); // For vertical scanning impl<'a> Area for TransposedArea<'a> { fn width(&self) -> usize { self.0.height } fn height(&self) -> usize { self.0.width } fn symbol_at(&self, x: usize, y: usize) -> Symbol { match self.0.chars[x][y] { '+' => Symbol::Corner, '|' => Symbol::Connect, _ => Symbol::Other } } } // Information about connections. struct Connections { lines: HashSet<Line>, points: HashMap<Point, u8> } pub fn count(lines: &[&str]) -> usize { if lines.len() == 0 { return 0 } else if lines[0].len() == 0 { return 0 } let area = RealArea { width: lines[0].len(), height: lines.len(), chars: lines.iter().map(|line| line.chars().collect()).collect() }; let area_transposed = TransposedArea(&area); let mut conns = scan_connected(&area); let conns_transposed = scan_connected(&area_transposed); // The transposed connections have their coordinate system wrong, // correct this. for l in conns_transposed.lines { conns.lines.insert(Line{x1: l.y1, y1: l.x1, x2: l.y2, y2: l.x2}); } for (p, tcf) in conns_transposed.points { let cf = conns.points.entry(Point{x: p.y, y: p.x}).or_insert(0); *cf = *cf | (tcf << 2) } let mut total = 0; for (tl_p, tl_cf) in conns.points.iter() { // top left point and connection flags if tl_cf & (CONN_RIGHT|CONN_DOWN) == CONN_RIGHT|CONN_DOWN { for (br_p, br_cf) in conns.points.iter() { // left, right, top, bottom of potential rectangle let l = tl_p.x; let r = br_p.x; let t = tl_p.y; let b = br_p.y; let is_rect = br_cf & (CONN_LEFT|CONN_UP) == CONN_LEFT|CONN_UP && r > l && b > t && conns.lines.contains(&Line{x1: l, y1: t, x2: l, y2: b}) && conns.lines.contains(&Line{x1: l, y1: t, x2: r, y2: t}) && conns.lines.contains(&Line{x1: l, y1: b, x2: r, y2: b}) && conns.lines.contains(&Line{x1: r, y1: t, x2: r, y2: b}); if is_rect { total += 1 } } } } return total; } fn scan_connected(area: &Area) -> Connections { let mut conns = Connections{ lines: HashSet::new(), points: HashMap::new() }; let mut connected: Vec<usize> = vec![]; for y in 0..area.height() { connected.clear(); for x in 0..area.width() { let sym = area.symbol_at(x, y); if sym == Symbol::Corner { for prev in connected.iter() { conns.lines.insert(Line{x1: prev.clone(), y1: y, x2: x, y2: y}); } if let Some(last) = connected.last() { let cf = conns.points.get_mut(&Point{x: last.clone(), y: y}).unwrap(); *cf = *cf | CONN_RIGHT; } let cf = conns.points.entry(Point{x: x, y: y}).or_insert(0); if connected.len() > 0 { *cf = *cf | CONN_LEFT; } connected.push(x); } else if sym != Symbol::Connect { connected.clear(); // End of connected bit. } } } conns } #}
填充/相关
Forth
1. Readme
Forth
为 Forth 的一个非常简单的子集,实现一个执行程序.
Forth是一种基于栈的编程语言。为 Forth 的一小部分子集,实现一个非常基本的执行器。
您的执行器必须支持以下关键字:
+
,-
,*
,/
(整数运算)DUP
,DROP
,SWAP
,OVER
(栈操作)
您的执行器还必须支持使用惯用语法,定义新单词:: word-name definition ;
.
为简单起见,您需要支持的唯一数据类型是,至少 16 位大小的有符号整数。
您应该对语法使用以下规则:一个 number 是一个或多个(ASCII)数字的序列,单词是一个或多个字母,数字,符号或标点符号的序列,而不是数字。(Forth 可能使用稍微不同的规则,但这已足够接近。)
单词不区分大小写。
2. 开始你的表演
pub type Value = i32; pub type ForthResult = Result<(), Error>; pub struct Forth; #[derive(Debug, PartialEq)] pub enum Error { DivisionByZero, StackUnderflow, UnknownWord, InvalidWord, } impl Forth { pub fn new() -> Forth { unimplemented!() } pub fn stack(&self) -> Vec<Value> { unimplemented!() } pub fn eval(&mut self, input: &str) -> ForthResult { unimplemented!("result of evaluating '{}'", input) } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn no_input_no_stack() { assert_eq!(Vec::<Value>::new(), Forth::new().stack()); } #[test] //#[ignore] fn numbers_just_get_pushed_onto_the_stack() { let mut f = Forth::new(); assert!(f.eval("1 2 3 4 5 -1").is_ok()); assert_eq!(vec![1, 2, 3, 4, 5, -1], f.stack()); } #[test] //#[ignore] fn non_word_characters_are_separators() { let mut f = Forth::new(); // Note the Ogham Space Mark ( ), this is a spacing character. assert!(f.eval("1\u{0000}2\u{0001}3\n4\r5 6\t7").is_ok()); assert_eq!(vec![1, 2, 3, 4, 5, 6, 7], f.stack()); } #[test] //#[ignore] fn basic_arithmetic_1() { let mut f = Forth::new(); assert!(f.eval("1 2 + 4 -").is_ok()); assert_eq!(vec![-1], f.stack()); } #[test] //#[ignore] fn basic_arithmetic_2() { let mut f = Forth::new(); assert!(f.eval("2 4 * 3 /").is_ok()); assert_eq!(vec![2], f.stack()); } #[test] //#[ignore] fn addition_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("+")); } #[test] //#[ignore] fn subtraction_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("-")); } #[test] //#[ignore] fn multiplication_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("*")); } #[test] //#[ignore] fn division_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("/")); } #[test] //#[ignore] fn division_by_zero() { let mut f = Forth::new(); assert_eq!(Err(Error::DivisionByZero), f.eval("4 2 2 - /")); } #[test] //#[ignore] fn dup() { let mut f = Forth::new(); assert!(f.eval("1 DUP").is_ok()); assert_eq!(vec![1, 1], f.stack()); } #[test] //#[ignore] fn dup_case_insensitive() { let mut f = Forth::new(); assert!(f.eval("1 Dup").is_ok()); assert_eq!(vec![1, 1], f.stack()); } #[test] //#[ignore] fn dup_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("dup")); } #[test] //#[ignore] fn drop() { let mut f = Forth::new(); assert!(f.eval("1 drop").is_ok()); assert_eq!(Vec::<Value>::new(), f.stack()); } #[test] //#[ignore] fn drop_with_two() { let mut f = Forth::new(); assert!(f.eval("1 2 drop").is_ok()); assert_eq!(vec![1], f.stack()); } #[test] //#[ignore] fn drop_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("drop")); } #[test] //#[ignore] fn swap() { let mut f = Forth::new(); assert!(f.eval("1 2 swap").is_ok()); assert_eq!(vec![2, 1], f.stack()); } #[test] //#[ignore] fn swap_with_three() { let mut f = Forth::new(); assert!(f.eval("1 2 3 swap").is_ok()); assert_eq!(vec![1, 3, 2], f.stack()); } #[test] //#[ignore] fn swap_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap")); assert_eq!(Err(Error::StackUnderflow), f.eval("swap")); } #[test] //#[ignore] fn over() { let mut f = Forth::new(); assert!(f.eval("1 2 over").is_ok()); assert_eq!(vec![1, 2, 1], f.stack()); } #[test] //#[ignore] fn over_with_three() { let mut f = Forth::new(); assert!(f.eval("1 2 3 over").is_ok()); assert_eq!(vec![1, 2, 3, 2], f.stack()); } #[test] //#[ignore] fn over_error() { let mut f = Forth::new(); assert_eq!(Err(Error::StackUnderflow), f.eval("1 over")); assert_eq!(Err(Error::StackUnderflow), f.eval("over")); } #[test] //#[ignore] fn defining_a_new_word() { let mut f = Forth::new(); assert!(f.eval(": CoUnT 1 2 3 ;").is_ok()); assert!(f.eval("count COUNT").is_ok()); assert_eq!(vec![1, 2, 3, 1, 2, 3], f.stack()); } #[test] //#[ignore] fn redefining_an_existing_word() { let mut f = Forth::new(); assert!(f.eval(": foo dup ;").is_ok()); assert!(f.eval(": foo dup dup ;").is_ok()); assert!(f.eval("1 foo").is_ok()); assert_eq!(vec![1, 1, 1], f.stack()); } #[test] //#[ignore] fn redefining_an_existing_built_in_word() { let mut f = Forth::new(); assert!(f.eval(": swap dup ;").is_ok()); assert!(f.eval("1 swap").is_ok()); assert_eq!(vec![1, 1], f.stack()); } #[test] //#[ignore] fn defining_words_with_odd_characters() { let mut f = Forth::new(); assert!(f.eval(": € 220371 ; €").is_ok()); assert_eq!(vec![220371], f.stack()); } #[test] //#[ignore] fn defining_a_number() { let mut f = Forth::new(); assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;")); } #[test] //#[ignore] fn malformed_word_definition() { let mut f = Forth::new(); assert_eq!(Err(Error::InvalidWord), f.eval(":")); assert_eq!(Err(Error::InvalidWord), f.eval(": foo")); assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1")); } #[test] //#[ignore] fn calling_non_existing_word() { let mut f = Forth::new(); assert_eq!(Err(Error::UnknownWord), f.eval("1 foo")); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; use std::collections::LinkedList; use std::str::FromStr; use Term::*; pub type Value = i32; pub type ForthResult = Result<(), Error>; type StackResult<T> = Result<T, Error>; type Stack = LinkedList<Value>; type Code = LinkedList<Term>; type Definitions = HashMap<String, Code>; pub struct Forth { code: Code, defs: Definitions, stack: Stack, } #[derive(Debug, PartialEq)] pub enum Error { DivisionByZero, StackUnderflow, UnknownWord, InvalidWord, } #[derive(Debug, Clone)] enum Term { Number(Value), Word(String), StartDefinition, EndDefinition, } impl FromStr for Term { type Err = (); fn from_str(s: &str) -> Result<Term, ()> { match s { ":" => Ok(StartDefinition), ";" => Ok(EndDefinition), _ => Err(()), }.or_else(|_| Value::from_str(s).map(Number)) .or_else(|_| Ok(Word(s.to_ascii_lowercase()))) } } impl Forth { pub fn new() -> Forth { Forth { code: LinkedList::new(), defs: HashMap::new(), stack: LinkedList::new(), } } pub fn stack(&self) -> Vec<Value> { self.stack.iter().cloned().collect() } pub fn eval(&mut self, input: &str) -> ForthResult { let mut new_code = Forth::into_code(input); self.code.append(&mut new_code); self.run() } fn run(&mut self) -> ForthResult { while let Some(term) = self.code.pop_front() { try!(self.step_term(term)) } Forth::ok() } fn step_term(&mut self, term: Term) -> ForthResult { match term { Number(value) => self.push(value), Word(word) => self.step_word(word), StartDefinition => self.store_definition(), EndDefinition => Err(Error::InvalidWord), } } fn step_word(&mut self, word: String) -> ForthResult { self.defs .get(&word) .ok_or(Error::UnknownWord) .map(Clone::clone) .map(|mut code| self.code.append(&mut code)) .or_else(|_| self.step_built_in(&word)) } fn step_built_in(&mut self, word: &String) -> ForthResult { match word.as_ref() { "+" => self.bin_op(|(a, b)| Ok(a + b)), "-" => self.bin_op(|(a, b)| Ok(a - b)), "*" => self.bin_op(|(a, b)| Ok(a * b)), "/" => self.bin_op(|(a, b)| a.checked_div(b).ok_or(Error::DivisionByZero)), "dup" => self.pop().and_then(|a| self.push(a).and(self.push(a))), "drop" => self.pop().and(Forth::ok()), "swap" => self.pop_two() .and_then(|(a, b)| self.push(b).and(self.push(a))), "over" => self.pop_two() .and_then(|(a, b)| self.push(a).and(self.push(b)).and(self.push(a))), _ => Err(Error::UnknownWord), } } fn store_definition(&mut self) -> ForthResult { let mut def = LinkedList::new(); loop { match self.code.pop_front() { Some(EndDefinition) => break, Some(term) => def.push_back(term), None => return Err(Error::InvalidWord), } } if let Some(Word(name)) = def.pop_front() { self.store_word(name, def) } else { Err(Error::InvalidWord) } } fn push(&mut self, value: Value) -> ForthResult { self.stack.push_back(value); Forth::ok() } fn pop(&mut self) -> StackResult<Value> { self.stack.pop_back().ok_or(Error::StackUnderflow) } fn pop_two(&mut self) -> StackResult<(Value, Value)> { self.pop().and_then(|b| self.pop().and_then(|a| Ok((a, b)))) } fn bin_op<F>(&mut self, op: F) -> ForthResult where F: FnOnce((Value, Value)) -> StackResult<Value>, { self.pop_two() .and_then(op) .and_then(|value| self.push(value)) } fn store_word(&mut self, name: String, code: Code) -> ForthResult { self.defs.insert(name, code); Forth::ok() } fn into_code(input: &str) -> LinkedList<Term> { input .split(|c: char| c.is_whitespace() || c.is_control()) .map(Term::from_str) .filter(Result::is_ok) .map(Result::unwrap) .collect() } fn ok() -> ForthResult { Ok(()) } } #}
填充/相关
Circular Buffer
1. Readme
循环缓冲区
循环缓冲区,或环形缓冲区是一种数据结构,它使用单个固定大小的缓冲区,就好像它是端到端连接一样。
循环缓冲区首先开始为空,并具有一些预定义的长度。例如,这是一个 7 元素的缓冲区:
[ ][ ][ ][ ][ ][ ][ ]
假设 1 被写入缓冲区的中间(其实精确的起始位置在循环缓冲区中无关紧要):
[ ][ ][ ][1][ ][ ][ ]
然后假设添加了另外两个元素 - 2 和 3 - 在 1 之后,附加:
[ ][ ][ ][1][2][3][ ]
如果从缓冲区中,删除了两个元素,则删除缓冲区内最旧的值。在这种情况下,删除的两个元素是 1 和 2,缓冲区只有 3:
[ ][ ][ ][ ][ ][3][ ]
如果缓冲区有 7 个元素,那么它就完全填满了:
[6][7][8][9][3][4][5]
当缓冲区已满时,将引发错误,警告客户端,进一步写入被阻止,直到插槽空闲为止。
当缓冲区已满时,客户端可以选择使用强制写入,覆盖最旧的数据。在这种情况下,添加了另外两个元素 - A 和 B - 它们会覆盖 3 和 4:
[6][7][8][9][A][B][5]
3 和 4 已被 A 和 B 取代,使得 5 现在是缓冲区中最旧的数据。最后,如果删除了两个元素,那么返回的是 5 和 6,和剩余的缓冲区:
[ ][7][8][9][A][B][ ]
因为有可用的空间,但如果客户端再次覆盖存储 C 和 D,那么先前存储 5 和 6 的空间将被使用,而不是 7 和 8 的位置。7 仍然是最旧的元素,缓冲区又是充分.
[D][7][8][9][A][B][C]
资源
维基百科http://en.wikipedia.org/wiki/Circular_Buffer
2. 开始你的表演
use std::marker::PhantomData; pub struct CircularBuffer<T> { // This field is here to make the template compile and not to // complain about unused type parameter 'T'. Once you start // solving the exercise, delete this field and the 'std::marker::PhantomData' // import. field: PhantomData<T>, } #[derive(Debug, PartialEq)] pub enum Error { EmptyBuffer, FullBuffer, } impl<T> CircularBuffer<T> { pub fn new(capacity: usize) -> Self { unimplemented!( "Construct a new CircularBuffer with the capacity to hold {}.", match capacity { 1 => format!("1 element"), _ => format!("{} elements", capacity), } ); } pub fn write(&mut self, _element: T) -> Result<(), Error> { unimplemented!("Write the passed element to the CircularBuffer or return FullBuffer error if CircularBuffer is full."); } pub fn read(&mut self) -> Result<T, Error> { unimplemented!("Read the oldest element from the CircularBuffer or return EmptyBuffer error if CircularBuffer is empty."); } pub fn clear(&mut self) { unimplemented!("Clear the CircularBuffer."); } pub fn overwrite(&mut self, _element: T) { unimplemented!("Write the passed element to the CircularBuffer, overwriting the existing elements if CircularBuffer is full."); } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn error_on_read_empty_buffer() { let mut buffer = CircularBuffer::<char>::new(1); assert_eq!(Err(Error::EmptyBuffer), buffer.read()); } #[test] //#[ignore] fn write_and_read_back_item() { let mut buffer = CircularBuffer::new(1); assert!(buffer.write('1').is_ok()); assert_eq!(Ok('1'), buffer.read()); assert_eq!(Err(Error::EmptyBuffer), buffer.read()); } #[test] //#[ignore] fn write_and_read_back_multiple_items() { let mut buffer = CircularBuffer::new(2); assert!(buffer.write('1').is_ok()); assert!(buffer.write('2').is_ok()); assert_eq!(Ok('1'), buffer.read()); assert_eq!(Ok('2'), buffer.read()); assert_eq!(Err(Error::EmptyBuffer), buffer.read()); } #[test] //#[ignore] fn alternate_write_and_read() { let mut buffer = CircularBuffer::new(2); assert!(buffer.write('1').is_ok()); assert_eq!(Ok('1'), buffer.read()); assert!(buffer.write('2').is_ok()); assert_eq!(Ok('2'), buffer.read()); } #[test] //#[ignore] fn clear_buffer() { let mut buffer = CircularBuffer::new(3); assert!(buffer.write('1').is_ok()); assert!(buffer.write('2').is_ok()); assert!(buffer.write('3').is_ok()); buffer.clear(); assert_eq!(Err(Error::EmptyBuffer), buffer.read()); assert!(buffer.write('1').is_ok()); assert!(buffer.write('2').is_ok()); assert_eq!(Ok('1'), buffer.read()); assert!(buffer.write('3').is_ok()); assert_eq!(Ok('2'), buffer.read()); } #[test] //#[ignore] fn full_buffer_error() { let mut buffer = CircularBuffer::new(2); assert!(buffer.write('1').is_ok()); assert!(buffer.write('2').is_ok()); assert_eq!(Err(Error::FullBuffer), buffer.write('3')); } #[test] //#[ignore] fn overwrite_item_in_non_full_buffer() { let mut buffer = CircularBuffer::new(2); assert!(buffer.write('1').is_ok()); buffer.overwrite('2'); assert_eq!(Ok('1'), buffer.read()); assert_eq!(Ok('2'), buffer.read()); assert_eq!(Err(Error::EmptyBuffer), buffer.read()); } #[test] //#[ignore] fn overwrite_item_in_full_buffer() { let mut buffer = CircularBuffer::new(2); assert!(buffer.write('1').is_ok()); assert!(buffer.write('2').is_ok()); buffer.overwrite('A'); assert_eq!(Ok('2'), buffer.read()); assert_eq!(Ok('A'), buffer.read()); } #[test] //#[ignore] fn integer_buffer() { let mut buffer = CircularBuffer::new(2); assert!(buffer.write(1).is_ok()); assert!(buffer.write(2).is_ok()); assert_eq!(Ok(1), buffer.read()); assert!(buffer.write(-1).is_ok()); assert_eq!(Ok(2), buffer.read()); assert_eq!(Ok(-1), buffer.read()); assert_eq!(Err(Error::EmptyBuffer), buffer.read()); } #[test] //#[ignore] fn string_buffer() { let mut buffer = CircularBuffer::new(2); buffer.write("".to_string()).unwrap(); buffer.write("Testing".to_string()).unwrap(); assert_eq!(0, buffer.read().unwrap().len()); assert_eq!(Ok("Testing".to_string()), buffer.read()); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { #[derive(Debug, PartialEq)] pub enum Error { EmptyBuffer, FullBuffer, } pub struct CircularBuffer<T: Default + Clone> { buffer: Vec<T>, size: usize, start: usize, end: usize, } impl<T: Default + Clone> CircularBuffer<T> { // this circular buffer keeps an unallocated slot between the start and the end // when the buffer is full. pub fn new(size: usize) -> CircularBuffer<T> { CircularBuffer { buffer: vec![T::default(); size + 1], size: size + 1, start: 0, end: 0, } } pub fn read(&mut self) -> Result<T, Error> { if self.is_empty() { return Err(Error::EmptyBuffer); } let v = self.buffer.get(self.start).unwrap().clone(); self.advance_start(); Ok(v) } pub fn write(&mut self, byte: T) -> Result<(), Error> { if self.is_full() { return Err(Error::FullBuffer); } self.buffer[self.end] = byte; self.advance_end(); Ok(()) } pub fn overwrite(&mut self, byte: T) { if self.is_full() { self.advance_start(); } self.buffer[self.end] = byte; self.advance_end(); } pub fn clear(&mut self) { self.start = 0; self.end = 0; } pub fn is_empty(&self) -> bool { self.start == self.end } pub fn is_full(&self) -> bool { (self.end + 1) % self.size == self.start } fn advance_start(&mut self) { self.start = (self.start + 1) % self.size; } fn advance_end(&mut self) { self.end = (self.end + 1) % self.size; } } #}
填充/相关
React
1. Readme
React
实现基本的 React 系统。
React 式编程是一种编程范例,它着重于如何根据彼此来计算值,以允许对一个值的更改,自动传播到其他值,如在电子表格中。
实现一个基本的 React 系统,其中,有带可设置值的单元(”输入”单元),还有能根据其他单元计算值的单元(”计算”单元)。实现更新,以便当输入值改变时,(关联的)值会传播,直到一个新的稳定的系统状态。
此外,计算单元应该允许,注册更改通知的回调。当单元值,从之前的稳定状态到一个新的稳定状态时,调用一个单元格的回调。
2. 开始你的表演
/// `InputCellID` is a unique identifier for an input cell. #[derive(Clone, Copy, Debug, PartialEq)] pub struct InputCellID(); /// `ComputeCellID` is a unique identifier for a compute cell. /// Values of type `InputCellID` and `ComputeCellID` should not be mutually assignable, /// demonstrated by the following tests: /// /// ```compile_fail /// let mut r = react::Reactor::new(); /// let input: react::ComputeCellID = r.create_input(111); /// ``` /// /// ```compile_fail /// let mut r = react::Reactor::new(); /// let input = r.create_input(111); /// let compute: react::InputCellID = r.create_compute(&[react::CellID::Input(input)], |_| 222).unwrap(); /// ``` #[derive(Clone, Copy, Debug, PartialEq)] pub struct ComputeCellID(); #[derive(Clone, Copy, Debug, PartialEq)] pub struct CallbackID(); #[derive(Clone, Copy, Debug, PartialEq)] pub enum CellID { Input(InputCellID), Compute(ComputeCellID), } #[derive(Debug, PartialEq)] pub enum RemoveCallbackError { NonexistentCell, NonexistentCallback, } pub struct Reactor<T> { // Just so that the compiler doesn't complain about an unused type parameter. // You probably want to delete this field. dummy: ::std::marker::PhantomData<T>, } // You are guaranteed that Reactor will only be tested against types that are Copy + PartialEq. impl<T: Copy + PartialEq> Reactor<T> { pub fn new() -> Self { unimplemented!() } // Creates an input cell with the specified initial value, returning its ID. pub fn create_input(&mut self, _initial: T) -> InputCellID { unimplemented!() } // Creates a compute cell with the specified dependencies and compute function. // The compute function is expected to take in its arguments in the same order as specified in // `dependencies`. // You do not need to reject compute functions that expect more arguments than there are // dependencies (how would you check for this, anyway?). // // If any dependency doesn't exist, returns an Err with that nonexistent dependency. // (If multiple dependencies do not exist, exactly which one is returned is not defined and // will not be tested) // // Notice that there is no way to *remove* a cell. // This means that you may assume, without checking, that if the dependencies exist at creation // time they will continue to exist as long as the Reactor exists. pub fn create_compute<F: Fn(&[T]) -> T>( &mut self, _dependencies: &[CellID], _compute_func: F, ) -> Result<ComputeCellID, CellID> { unimplemented!() } // Retrieves the current value of the cell, or None if the cell does not exist. // // You may wonder whether it is possible to implement `get(&self, id: CellID) -> Option<&Cell>` // and have a `value(&self)` method on `Cell`. // // It turns out this introduces a significant amount of extra complexity to this exercise. // We chose not to cover this here, since this exercise is probably enough work as-is. pub fn value(&self, id: CellID) -> Option<T> { unimplemented!("Get the value of the cell whose id is {:?}", id) } // Sets the value of the specified input cell. // // Returns false if the cell does not exist. // // Similarly, you may wonder about `get_mut(&mut self, id: CellID) -> Option<&mut Cell>`, with // a `set_value(&mut self, new_value: T)` method on `Cell`. // // As before, that turned out to add too much extra complexity. pub fn set_value(&mut self, _id: InputCellID, _new_value: T) -> bool { unimplemented!() } // Adds a callback to the specified compute cell. // // Returns the ID of the just-added callback, or None if the cell doesn't exist. // // Callbacks on input cells will not be tested. // // The semantics of callbacks (as will be tested): // For a single set_value call, each compute cell's callbacks should each be called: // * Zero times if the compute cell's value did not change as a result of the set_value call. // * Exactly once if the compute cell's value changed as a result of the set_value call. // The value passed to the callback should be the final value of the compute cell after the // set_value call. pub fn add_callback<F: FnMut(T) -> ()>( &mut self, _id: ComputeCellID, _callback: F, ) -> Option<CallbackID> { unimplemented!() } // Removes the specified callback, using an ID returned from add_callback. // // Returns an Err if either the cell or callback does not exist. // // A removed callback should no longer be called. pub fn remove_callback( &mut self, cell: ComputeCellID, callback: CallbackID, ) -> Result<(), RemoveCallbackError> { unimplemented!( "Remove the callback identified by the CallbackID {:?} from the cell {:?}", callback, cell, ) } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn input_cells_have_a_value() { let mut reactor = Reactor::new(); let input = reactor.create_input(10); assert_eq!(reactor.value(CellID::Input(input)), Some(10)); } #[test] //#[ignore] fn an_input_cells_value_can_be_set() { let mut reactor = Reactor::new(); let input = reactor.create_input(4); assert!(reactor.set_value(input, 20)); assert_eq!(reactor.value(CellID::Input(input)), Some(20)); } #[test] //#[ignore] fn error_setting_a_nonexistent_input_cell() { let mut dummy_reactor = Reactor::new(); let input = dummy_reactor.create_input(1); assert!(!Reactor::new().set_value(input, 0)); } #[test] //#[ignore] fn compute_cells_calculate_initial_value() { let mut reactor = Reactor::new(); let input = reactor.create_input(1); let output = reactor .create_compute(&[CellID::Input(input)], |v| v[0] + 1) .unwrap(); assert_eq!(reactor.value(CellID::Compute(output)), Some(2)); } #[test] //#[ignore] fn compute_cells_take_inputs_in_the_right_order() { let mut reactor = Reactor::new(); let one = reactor.create_input(1); let two = reactor.create_input(2); let output = reactor .create_compute(&[CellID::Input(one), CellID::Input(two)], |v| { v[0] + v[1] * 10 }) .unwrap(); assert_eq!(reactor.value(CellID::Compute(output)), Some(21)); } #[test] //#[ignore] fn error_creating_compute_cell_if_input_doesnt_exist() { let mut dummy_reactor = Reactor::new(); let input = dummy_reactor.create_input(1); assert_eq!( Reactor::new().create_compute(&[CellID::Input(input)], |_| 0), Err(CellID::Input(input)) ); } #[test] //#[ignore] fn do_not_break_cell_if_creating_compute_cell_with_valid_and_invalid_input() { let mut dummy_reactor = Reactor::new(); let _ = dummy_reactor.create_input(1); let dummy_cell = dummy_reactor.create_input(2); let mut reactor = Reactor::new(); let input = reactor.create_input(1); assert_eq!( reactor.create_compute(&[CellID::Input(input), CellID::Input(dummy_cell)], |_| 0), Err(CellID::Input(dummy_cell)) ); assert!(reactor.set_value(input, 5)); assert_eq!(reactor.value(CellID::Input(input)), Some(5)); } #[test] //#[ignore] fn compute_cells_update_value_when_dependencies_are_changed() { let mut reactor = Reactor::new(); let input = reactor.create_input(1); let output = reactor .create_compute(&[CellID::Input(input)], |v| v[0] + 1) .unwrap(); assert_eq!(reactor.value(CellID::Compute(output)), Some(2)); assert!(reactor.set_value(input, 3)); assert_eq!(reactor.value(CellID::Compute(output)), Some(4)); } #[test] //#[ignore] fn compute_cells_can_depend_on_other_compute_cells() { let mut reactor = Reactor::new(); let input = reactor.create_input(1); let times_two = reactor .create_compute(&[CellID::Input(input)], |v| v[0] * 2) .unwrap(); let times_thirty = reactor .create_compute(&[CellID::Input(input)], |v| v[0] * 30) .unwrap(); let output = reactor .create_compute( &[CellID::Compute(times_two), CellID::Compute(times_thirty)], |v| v[0] + v[1], ) .unwrap(); assert_eq!(reactor.value(CellID::Compute(output)), Some(32)); assert!(reactor.set_value(input, 3)); assert_eq!(reactor.value(CellID::Compute(output)), Some(96)); } /// A CallbackRecorder helps tests whether callbacks get called correctly. /// You'll see it used in tests that deal with callbacks. /// The names should be descriptive enough so that the tests make sense, /// so it's not necessary to fully understand the implementation, /// though you are welcome to. struct CallbackRecorder { // Note that this `Cell` is https://doc.rust-lang.org/std/cell/ // a mechanism to allow internal mutability, // distinct from the cells (input cells, compute cells) in the reactor value: std::cell::Cell<Option<i32>>, } impl CallbackRecorder { fn new() -> Self { CallbackRecorder { value: std::cell::Cell::new(None), } } fn expect_to_have_been_called_with(&self, v: i32) { assert_ne!( self.value.get(), None, "Callback was not called, but should have been" ); assert_eq!( self.value.replace(None), Some(v), "Callback was called with incorrect value" ); } fn expect_not_to_have_been_called(&self) { assert_eq!( self.value.get(), None, "Callback was called, but should not have been" ); } fn callback_called(&self, v: i32) { assert_eq!( self.value.replace(Some(v)), None, "Callback was called too many times; can't be called with {}", v ); } } #[test] //#[ignore] fn compute_cells_fire_callbacks() { let cb = CallbackRecorder::new(); let mut reactor = Reactor::new(); let input = reactor.create_input(1); let output = reactor .create_compute(&[CellID::Input(input)], |v| v[0] + 1) .unwrap(); assert!(reactor .add_callback(output, |v| cb.callback_called(v)) .is_some()); assert!(reactor.set_value(input, 3)); cb.expect_to_have_been_called_with(4); } #[test] //#[ignore] fn error_adding_callback_to_nonexistent_cell() { let mut dummy_reactor = Reactor::new(); let input = dummy_reactor.create_input(1); let output = dummy_reactor .create_compute(&[CellID::Input(input)], |_| 0) .unwrap(); assert_eq!( Reactor::new().add_callback(output, |_: u32| println!("hi")), None ); } #[test] //#[ignore] fn callbacks_only_fire_on_change() { let cb = CallbackRecorder::new(); let mut reactor = Reactor::new(); let input = reactor.create_input(1); let output = reactor .create_compute( &[CellID::Input(input)], |v| if v[0] < 3 { 111 } else { 222 }, ) .unwrap(); assert!(reactor .add_callback(output, |v| cb.callback_called(v)) .is_some()); assert!(reactor.set_value(input, 2)); cb.expect_not_to_have_been_called(); assert!(reactor.set_value(input, 4)); cb.expect_to_have_been_called_with(222); } #[test] //#[ignore] fn callbacks_can_be_added_and_removed() { let cb1 = CallbackRecorder::new(); let cb2 = CallbackRecorder::new(); let cb3 = CallbackRecorder::new(); let mut reactor = Reactor::new(); let input = reactor.create_input(11); let output = reactor .create_compute(&[CellID::Input(input)], |v| v[0] + 1) .unwrap(); let callback = reactor .add_callback(output, |v| cb1.callback_called(v)) .unwrap(); assert!(reactor .add_callback(output, |v| cb2.callback_called(v)) .is_some()); assert!(reactor.set_value(input, 31)); cb1.expect_to_have_been_called_with(32); cb2.expect_to_have_been_called_with(32); assert!(reactor.remove_callback(output, callback).is_ok()); assert!(reactor .add_callback(output, |v| cb3.callback_called(v)) .is_some()); assert!(reactor.set_value(input, 41)); cb1.expect_not_to_have_been_called(); cb2.expect_to_have_been_called_with(42); cb3.expect_to_have_been_called_with(42); } #[test] //#[ignore] fn removing_a_callback_multiple_times_doesnt_interfere_with_other_callbacks() { let cb1 = CallbackRecorder::new(); let cb2 = CallbackRecorder::new(); let mut reactor = Reactor::new(); let input = reactor.create_input(1); let output = reactor .create_compute(&[CellID::Input(input)], |v| v[0] + 1) .unwrap(); let callback = reactor .add_callback(output, |v| cb1.callback_called(v)) .unwrap(); assert!(reactor .add_callback(output, |v| cb2.callback_called(v)) .is_some()); // We want the first remove to be Ok, but the others should be errors. assert!(reactor.remove_callback(output, callback).is_ok()); for _ in 1..5 { assert_eq!( reactor.remove_callback(output, callback), Err(RemoveCallbackError::NonexistentCallback) ); } assert!(reactor.set_value(input, 2)); cb1.expect_not_to_have_been_called(); cb2.expect_to_have_been_called_with(3); } #[test] //#[ignore] fn callbacks_should_only_be_called_once_even_if_multiple_dependencies_change() { let cb = CallbackRecorder::new(); let mut reactor = Reactor::new(); let input = reactor.create_input(1); let plus_one = reactor .create_compute(&[CellID::Input(input)], |v| v[0] + 1) .unwrap(); let minus_one1 = reactor .create_compute(&[CellID::Input(input)], |v| v[0] - 1) .unwrap(); let minus_one2 = reactor .create_compute(&[CellID::Compute(minus_one1)], |v| v[0] - 1) .unwrap(); let output = reactor .create_compute( &[CellID::Compute(plus_one), CellID::Compute(minus_one2)], |v| v[0] * v[1], ) .unwrap(); assert!(reactor .add_callback(output, |v| cb.callback_called(v)) .is_some()); assert!(reactor.set_value(input, 4)); cb.expect_to_have_been_called_with(10); } #[test] //#[ignore] fn callbacks_should_not_be_called_if_dependencies_change_but_output_value_doesnt_change() { let cb = CallbackRecorder::new(); let mut reactor = Reactor::new(); let input = reactor.create_input(1); let plus_one = reactor .create_compute(&[CellID::Input(input)], |v| v[0] + 1) .unwrap(); let minus_one = reactor .create_compute(&[CellID::Input(input)], |v| v[0] - 1) .unwrap(); let always_two = reactor .create_compute( &[CellID::Compute(plus_one), CellID::Compute(minus_one)], |v| v[0] - v[1], ) .unwrap(); assert!(reactor .add_callback(always_two, |v| cb.callback_called(v)) .is_some()); for i in 2..5 { assert!(reactor.set_value(input, i)); cb.expect_not_to_have_been_called(); } } #[test] //#[ignore] fn test_adder_with_boolean_values() { // This is a digital logic circuit called an adder: // https://en.wikipedia.org/wiki/Adder_(electronics) let mut reactor = Reactor::new(); let a = reactor.create_input(false); let b = reactor.create_input(false); let carry_in = reactor.create_input(false); let a_xor_b = reactor .create_compute(&[CellID::Input(a), CellID::Input(b)], |v| v[0] ^ v[1]) .unwrap(); let sum = reactor .create_compute(&[CellID::Compute(a_xor_b), CellID::Input(carry_in)], |v| { v[0] ^ v[1] }) .unwrap(); let a_xor_b_and_cin = reactor .create_compute(&[CellID::Compute(a_xor_b), CellID::Input(carry_in)], |v| { v[0] && v[1] }) .unwrap(); let a_and_b = reactor .create_compute(&[CellID::Input(a), CellID::Input(b)], |v| v[0] && v[1]) .unwrap(); let carry_out = reactor .create_compute( &[CellID::Compute(a_xor_b_and_cin), CellID::Compute(a_and_b)], |v| v[0] || v[1], ) .unwrap(); let tests = &[ (false, false, false, false, false), (false, false, true, false, true), (false, true, false, false, true), (false, true, true, true, false), (true, false, false, false, true), (true, false, true, true, false), (true, true, false, true, false), (true, true, true, true, true), ]; for &(aval, bval, cinval, expected_cout, expected_sum) in tests { assert!(reactor.set_value(a, aval)); assert!(reactor.set_value(b, bval)); assert!(reactor.set_value(carry_in, cinval)); assert_eq!(reactor.value(CellID::Compute(sum)), Some(expected_sum)); assert_eq!( reactor.value(CellID::Compute(carry_out)), Some(expected_cout) ); } } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; /// `InputCellID` is a unique identifier for an input cell. #[derive(Clone, Copy, Debug, PartialEq)] pub struct InputCellID(usize); /// `ComputeCellID` is a unique identifier for a compute cell. /// Values of type `InputCellID` and `ComputeCellID` should not be mutually assignable, /// demonstrated by the following tests: /// /// ```compile_fail /// let mut r = react::Reactor::new(); /// let input: react::ComputeCellID = r.create_input(111); /// ``` /// /// ```compile_fail /// let mut r = react::Reactor::new(); /// let input = r.create_input(111); /// let compute: react::InputCellID = r.create_compute(&[react::CellID::Input(input)], |_| 222).unwrap(); /// ``` #[derive(Clone, Copy, Debug, PartialEq)] pub struct ComputeCellID(usize); #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct CallbackID(usize); #[derive(Clone, Copy, Debug, PartialEq)] pub enum CellID { Input(InputCellID), Compute(ComputeCellID), } #[derive(Debug, PartialEq)] pub enum RemoveCallbackError { NonexistentCell, NonexistentCallback, } struct Cell<T: Copy> { value: T, last_value: T, dependents: Vec<ComputeCellID>, } struct ComputeCell<'a, T: Copy> { cell: Cell<T>, dependencies: Vec<CellID>, f: Box<Fn(&[T]) -> T + 'a>, callbacks_issued: usize, callbacks: HashMap<CallbackID, Box<FnMut(T) -> () + 'a>>, } impl<T: Copy> Cell<T> { fn new(initial: T) -> Self { Cell { value: initial, last_value: initial, dependents: Vec::new(), } } } impl<'a, T: Copy> ComputeCell<'a, T> { fn new<F: Fn(&[T]) -> T + 'a>(initial: T, dependencies: Vec<CellID>, f: F) -> Self { ComputeCell { cell: Cell::new(initial), dependencies, f: Box::new(f), callbacks_issued: 0, callbacks: HashMap::new(), } } } pub struct Reactor<'a, T: Copy> { inputs: Vec<Cell<T>>, computes: Vec<ComputeCell<'a, T>>, } impl<'a, T: Copy + PartialEq> Reactor<'a, T> { pub fn new() -> Self { Reactor { inputs: Vec::new(), computes: Vec::new(), } } pub fn create_input(&mut self, initial: T) -> InputCellID { self.inputs.push(Cell::new(initial)); InputCellID(self.inputs.len() - 1) } pub fn create_compute<F: Fn(&[T]) -> T + 'a>( &mut self, dependencies: &[CellID], compute_func: F, ) -> Result<ComputeCellID, CellID> { // Check all dependencies' validity before modifying any of them, // so that we don't perform an incorrect partial write. for &dep in dependencies { match dep { CellID::Input(InputCellID(id)) => if id >= self.inputs.len() { return Err(dep); }, CellID::Compute(ComputeCellID(id)) => if id >= self.computes.len() { return Err(dep); }, } } let new_id = ComputeCellID(self.computes.len()); for &dep in dependencies { match dep { CellID::Input(InputCellID(id)) => self.inputs[id].dependents.push(new_id), CellID::Compute(ComputeCellID(id)) => { self.computes[id].cell.dependents.push(new_id) } } } let inputs: Vec<_> = dependencies .iter() .map(|&id| self.value(id).unwrap()) .collect(); let initial = compute_func(&inputs); self.computes.push(ComputeCell::new( initial, dependencies.to_vec(), compute_func, )); Ok(new_id) } pub fn value(&self, id: CellID) -> Option<T> { match id { CellID::Input(InputCellID(id)) => self.inputs.get(id).map(|c| c.value), CellID::Compute(ComputeCellID(id)) => self.computes.get(id).map(|c| c.cell.value), } } pub fn set_value(&mut self, id: InputCellID, new_value: T) -> bool { let InputCellID(id) = id; self.inputs .get_mut(id) .map(|c| { c.value = new_value; c.dependents.clone() }) .map(|deps| { for &d in deps.iter() { self.update_dependent(d); } // We can only fire callbacks after all dependents have updated. // So we can't combine this for loop with the one above! for d in deps { self.fire_callbacks(d); } }) .is_some() } pub fn add_callback<F: FnMut(T) -> () + 'a>( &mut self, id: ComputeCellID, callback: F, ) -> Option<CallbackID> { let ComputeCellID(id) = id; self.computes.get_mut(id).map(|c| { c.callbacks_issued += 1; let cbid = CallbackID(c.callbacks_issued); c.callbacks.insert(cbid, Box::new(callback)); cbid }) } pub fn remove_callback( &mut self, cell: ComputeCellID, callback: CallbackID, ) -> Result<(), RemoveCallbackError> { let ComputeCellID(cell) = cell; match self.computes.get_mut(cell) { Some(c) => match c.callbacks.remove(&callback) { Some(_) => Ok(()), None => Err(RemoveCallbackError::NonexistentCallback), }, None => Err(RemoveCallbackError::NonexistentCell), } } fn update_dependent(&mut self, id: ComputeCellID) { let ComputeCellID(id) = id; let (new_value, dependents) = { // This block limits the scope of the self.cells borrow. // This is necessary because we borrow it mutably below. let (dependencies, f, dependents) = match self.computes.get(id) { Some(c) => (&c.dependencies, &c.f, c.cell.dependents.clone()), None => panic!("Cell to update disappeared while querying"), }; let inputs: Vec<_> = dependencies .iter() .map(|&id| self.value(id).unwrap()) .collect(); (f(&inputs), dependents) }; match self.computes.get_mut(id) { Some(c) => { if c.cell.value == new_value { // No change here, we don't need to update our dependents. // (It wouldn't hurt to, but it would be unnecessary work) return; } c.cell.value = new_value; } None => panic!("Cell to update disappeared while updating"), } for d in dependents { self.update_dependent(d); } } fn fire_callbacks(&mut self, id: ComputeCellID) { let ComputeCellID(id) = id; let dependents = match self.computes.get_mut(id) { Some(c) => { if c.cell.value == c.cell.last_value { // Value hasn't changed since last callback fire. // We thus shouldn't fire the callbacks. return; } for cb in c.callbacks.values_mut() { cb(c.cell.value); } c.cell.last_value = c.cell.value; c.cell.dependents.clone() } None => panic!("Callback cell disappeared"), }; for d in dependents { self.fire_callbacks(d); } } } #}
填充/相关
未标签
hexadecimal
1. Readme
十六进制
使用第一原理,将用字符串表示的十六进制数,(例如”10af8c”),转换为其十进制等效数(即,不可以使用内置或外部库来完成转换)。
在 Web 上,我们使用十六进制表示颜色,例如( green: 008000, teal: 008080, navy: 000080)。
程序应该处理无效十六进制字符串。
资源
计算机科学的所有http://www.wolframalpha.com/examples/NumberBases.html
2. 开始你的表演
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_hex_1_is_decimal_1() { assert_eq!(Some(1), hex_to_int("1")); } #[test] //#[ignore] fn test_hex_c_is_decimal_12() { assert_eq!(Some(12), hex_to_int("c")); } #[test] //#[ignore] fn test_hex_10_is_decimal_16() { assert_eq!(Some(16), hex_to_int("10")); } #[test] //#[ignore] fn test_hex_af_is_decimal_175() { assert_eq!(Some(175), hex_to_int("af")); } #[test] //#[ignore] fn test_hex_100_is_decimal_256() { assert_eq!(Some(256), hex_to_int("100")); } #[test] //#[ignore] fn test_hex_19ace_is_decimal_105166() { assert_eq!(Some(105166), hex_to_int("19ace")); } #[test] //#[ignore] fn test_invalid_hex_is_none() { assert_eq!(None, hex_to_int("carrot")); } #[test] //#[ignore] fn test_black() { assert_eq!(Some(0), hex_to_int("0000000")); } #[test] //#[ignore] fn test_white() { assert_eq!(Some(16777215), hex_to_int("ffffff")); } #[test] //#[ignore] fn test_yellow() { assert_eq!(Some(16776960), hex_to_int("ffff00")); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { fn parse_hex_digit(c: char) -> Option<i64> { match c { '0' => Some(0), '1' => Some(1), '2' => Some(2), '3' => Some(3), '4' => Some(4), '5' => Some(5), '6' => Some(6), '7' => Some(7), '8' => Some(8), '9' => Some(9), 'a' => Some(10), 'b' => Some(11), 'c' => Some(12), 'd' => Some(13), 'e' => Some(14), 'f' => Some(15), _ => None, } } pub fn hex_to_int(string: &str) -> Option<i64> { let base: i64 = 16; string .chars() .rev() .enumerate() .fold(Some(0), |acc, (pos, c)| { parse_hex_digit(c).and_then(|n| acc.map(|acc| acc + n * base.pow(pos as u32))) }) } #}
填充/相关
nucleotide-codons
1. Readme
核苷酸密码子
编写一个函数,返回一个特定密码子的氨基酸名称,可能使用速记,编码.
在 DNA 序列中,3 个核苷酸称为密码子,编码氨基酸。通常密码子编码相同的氨基酸。国际纯和应用化学联盟开发了一个简写系统,用于命名密码子组,其编码为相同氨基酸。
简单地说,他们把四个字母 A、C、G 和 T 扩展成一堆代表不同可能性的字母。例如 R 代表 A 和 G, 所以 TAR 可表示为 TAA 和 TAG (把 “TAR” 当成正则式形式的 “TA[AG]”)。
编写一些代码,给出一个密码子,可以使用速记,然后返回密码子所编码的氨基酸的名称。您将得到一个非速记密码/名称对的列表,可作为您计算的基础。
见:维基百科.
2. 开始你的表演
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_methionine() { let info = parse(make_pairs()); assert_eq!(info.name_for("ATG"), Ok("methionine")); } #[test] //#[ignore] fn test_cysteine_tgt() { let info = parse(make_pairs()); assert_eq!(info.name_for("TGT"), Ok("cysteine")); } #[test] //#[ignore] fn test_cysteine_tgy() { // "compressed" name for TGT and TGC let info = parse(make_pairs()); assert_eq!(info.name_for("TGT"), info.name_for("TGY")); assert_eq!(info.name_for("TGC"), info.name_for("TGY")); } #[test] //#[ignore] fn test_stop() { let info = parse(make_pairs()); assert_eq!(info.name_for("TAA"), Ok("stop codon")); } #[test] //#[ignore] fn test_valine() { let info = parse(make_pairs()); assert_eq!(info.name_for("GTN"), Ok("valine")); } #[test] //#[ignore] fn test_isoleucine() { let info = parse(make_pairs()); assert_eq!(info.name_for("ATH"), Ok("isoleucine")); } #[test] //#[ignore] fn test_arginine_name() { // In arginine CGA can be "compressed" both as CGN and as MGR let info = parse(make_pairs()); assert_eq!(info.name_for("CGA"), Ok("arginine")); assert_eq!(info.name_for("CGN"), Ok("arginine")); assert_eq!(info.name_for("MGR"), Ok("arginine")); } #[test] //#[ignore] fn empty_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("").is_err()); } #[test] //#[ignore] fn x_is_not_shorthand_so_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("VWX").is_err()); } #[test] //#[ignore] fn too_short_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("AT").is_err()); } #[test] //#[ignore] fn too_long_is_invalid() { let info = parse(make_pairs()); assert!(info.name_for("ATTA").is_err()); } // The input data constructor. Returns a list of codon, name pairs. fn make_pairs() -> Vec<(&'static str, &'static str)> { let grouped = vec![ ("isoleucine", vec!["ATT", "ATC", "ATA"]), ("leucine", vec!["CTT", "CTC", "CTA", "CTG", "TTA", "TTG"]), ("valine", vec!["GTT", "GTC", "GTA", "GTG"]), ("phenylalanine", vec!["TTT", "TTC"]), ("methionine", vec!["ATG"]), ("cysteine", vec!["TGT", "TGC"]), ("alanine", vec!["GCT", "GCC", "GCA", "GCG"]), ("glycine", vec!["GGT", "GGC", "GGA", "GGG"]), ("proline", vec!["CCT", "CCC", "CCA", "CCG"]), ("threonine", vec!["ACT", "ACC", "ACA", "ACG"]), ("serine", vec!["TCT", "TCC", "TCA", "TCG", "AGT", "AGC"]), ("tyrosine", vec!["TAT", "TAC"]), ("tryptophan", vec!["TGG"]), ("glutamine", vec!["CAA", "CAG"]), ("asparagine", vec!["AAT", "AAC"]), ("histidine", vec!["CAT", "CAC"]), ("glutamic acid", vec!["GAA", "GAG"]), ("aspartic acid", vec!["GAT", "GAC"]), ("lysine", vec!["AAA", "AAG"]), ("arginine", vec!["CGT", "CGC", "CGA", "CGG", "AGA", "AGG"]), ("stop codon", vec!["TAA", "TAG", "TGA"]), ]; let mut pairs = Vec::<(&'static str, &'static str)>::new(); for (name, codons) in grouped.into_iter() { for codon in codons { pairs.push((codon, name)); } } pairs.sort_by(|&(_, a), &(_, b)| a.cmp(b)); return pairs; } #}
4. 答案
# #![allow(unused_variables)] #fn main() { use std::collections::HashMap; pub struct CodonInfo<'a> { actual_codons: HashMap<&'a str, &'a str>, } pub fn parse<'a>(pairs: Vec<(&'a str, &'a str)>) -> CodonInfo<'a> { CodonInfo { actual_codons: pairs.into_iter().collect(), } } impl<'a> CodonInfo<'a> { pub fn name_for(&self, codon: &str) -> Result<&'a str, &'static str> { if codon.len() != 3 { return Err("invalid length"); } let mut valid = true; let lookup: String = codon .chars() .map(|l| { // Get an example of a "letter" represented by the possibly encoded letter. // Since every codon represented by the compressed notation has to be of // the desired amino acid just picking one at random will do. match l { 'A' | 'W' | 'M' | 'R' | 'D' | 'H' | 'V' | 'N' => 'A', 'C' | 'S' | 'Y' | 'B' => 'C', 'G' | 'K' => 'G', 'T' => 'T', _ => { valid = false; ' ' } } }) .collect(); if !valid { return Err("invalid char"); } // If the input table is correct (which it is) every valid codon is in it // so unwrap() shouldn't panic. Ok(self.actual_codons.get(&lookup.as_ref()).unwrap()) } } #}
填充/相关
two-fer
1. Readme
Two Fer
Two-fer
或2-fer
是 two for one 的缩写。One for you and one for me。
"One for X, one for me."
当 X 是一个名字或you
。
如果给定名称是Alice
,结果应该是One for Alice, one for me.
。如果没有给出名称,结果应该是One for you, one for me.
。
资源
https://en.wikipedia.org/wiki/Two-fer
2. 开始你的表演
pub fn twofer(name: &str) -> String { unimplemented!("One for {}, one for me.", name); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn empty_string() { assert_eq!(twofer(""), "One for you, one for me."); } #[test] //#[ignore] fn alice() { assert_eq!(twofer("Alice"), "One for Alice, one for me."); } #[test] //#[ignore] fn bob() { assert_eq!(twofer("Bob"), "One for Bob, one for me."); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub fn twofer(name: &str) -> String { match name { "" => "One for you, one for me.".to_string(), _ => format!("One for {}, one for me.", name), } } #}
填充/相关
改为使用Cargo test
正如万千生成工具,总是具有主题/css样式/模版
之类的概念
mdBook 也不例外
我们为了,让这份练习可在网页上运行并知道结果,借用了mdBook工具,它本身就具有Rust Code
html元素运行的功能
可惜,也就相当于Cargo run
,而我们要使用的是Cargo test
。下面进入到我们的修改之旅
目录
基础,给第一次使用者
安装就不讲了
- 初始化mdBook项目是这样的
mdBook init --theme
- 它的主题之类,放在
src/theme
,结构是这样的
- theme
- book.js # 默认的js改造
- favicon.png
- highlight.css
- highlight.js
- index.hbs # 默认的模版,引擎是hbs
- css
- chrome.css
- general.css
- print.css
- variables.css
主要的,代码修改,落在了book.js
中,它本身就是给rust代码 元素
,(重点是)加上可与rust游乐场联系和运行代码,并返回结果的 播放按钮。
入主题,使用过mdBook
book.js
这个代码文件本身有600多行,着重在使用
Cargo test
的部分
使用Rust游乐场的Cargo test
- 原代码
// ...
var params = {
version: "stable",
optimize: "0",
code: text
};
if (text.indexOf("#![feature") !== -1) {
params.version = "nightly";
}
result_block.innerText = "Running...";
// TODO cargo test
fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
headers: {
'Content-Type': "application/json",
},
method: 'POST',
mode: 'cors',
body: JSON.stringify(params)
})
-
修改
- 修改 POST的网址
- 修改 POST的body
var params = {
// version: "stable",
// optimize: "0",
code: text,
channel: "stable",
"mode":"debug",
"backtrace":false,
tests: true,
crateType: "lib"
};
if (text.indexOf("#![feature") !== -1) {
params.version = "nightly";
}
result_block.innerText = "Running...";
// TODO cargo test
// fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
fetch_with_timeout("https://play.rust-lang.org/execute", {
headers: {
'Content-Type': "application/json",
},
method: 'POST',
mode: 'cors',
body: JSON.stringify(params)
})
这些是怎么知道,
主要是通过游乐场,点击Test按钮后的调试器的流量检测。
合并(用户/答案代码)与测试代码
- mdBook会为哪些没有写入
fn main//...
的Rust代码,写上类似下面+
加号的代码
+ #![allow(unused_variables)]
+ fn main() {
#[test]
fn test_hello_world() {
assert_eq!("Hello, World!", hello());
}
+ }
那好,我们就在book.js
中,搞个去掉默认的函数
- 全局函数:移除默认的
main
function remove_default_main(text){
var tArr = text.split("\n")
if(tArr.slice(0,3).some(t =>t.trim() == "#![allow(unused_variables)]")){
return tArr.slice(3,-1).join("\n")
}
return text
}
- 测试代码与其他(用户/答案代码),最大的不同就是
#[test]
所有,我们为测试代码,找一个全局变量document
下的安身之所
为
document.mdBookTextCode
,book.js本身也为每个代码元素添加按钮,那正好用来检测哪个代码元素是测试代码
// Process playpen code blocks
Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) {
// 添加测试代码,到全局变量document
var codewithtest = playpen_text(pre_block)
if(codewithtest.includes("#[test")){ // 测试代码,简单检验
document.mdBookTextCode = document.mdBookTextCode || remove_default_main(codewithtest)
}
- 当点击运行代码的播放按钮,我们要适时加上测试代码
主要是(用户/答案代码)的播放按钮
// 播放按钮的点击事件
function run_rust_code(code_block) {
var result_block = code_block.querySelector(".result");
if (!result_block) {
result_block = document.createElement('code');
result_block.className = 'result hljs language-bash';
code_block.append(result_block);
}
let text = playpen_text(code_block);
// 如 不相等和 不包括测试代码,添加测试代码上去
if(text != document.mdBookTextCode && !text.includes(document.mdBookTextCode)){
text = text + '\n' + document.mdBookTextCode
}
// 说来也巧,我们的第一个修改也在这个点击事件,拼在一起看看
var params = {
code: text,
channel: "stable",
"mode":"debug",
"backtrace":false,
tests: true,
crateType: "lib"
};
if (text.indexOf("#![feature") !== -1) {
params.version = "nightly";
}
result_block.innerText = "Running...";
// fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
fetch_with_timeout("https://play.rust-lang.org/execute", {
headers: {
'Content-Type': "application/json",
},
method: 'POST',
mode: 'cors',
body: JSON.stringify(params)
})