最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,看来想准确理解MOVE的机制,还需要对Rust有深刻的理解,所以开始了Rust的快速入门学习。

看了一下网上有关Rust的介绍,都说它的学习曲线相当陡峭,曾一度被其吓着,后来发现Rust借鉴了Haskell等函数式编程语言的优点,而我以前专门学习过Haskell,经过一段时间的入门学习,我现在已经喜欢上这门神奇的语言。

入门资料我用官方的《The Rust Programming Language》,非常权威,配合着《Rust by example》这本书一起学习,效果非常不错。

学习任何一项技能最怕没有反馈,尤其是学英语、学编程的时候,一定要“用”,学习编程时有一个非常有用的网站,它就是“欧拉计划”,网址:

https://projecteuler.net

这个网站提供了几百道由易到难的数学问题,你可以用任何办法去解决它,当然主要还得靠编程,但编程语言不限,已经有Java、C#、Python、Lisp、Haskell等各种解法,当然直接用google搜索答案就没意思了。

学习Rust最好先把基本的语法和特性看过一遍,然后就可以动手解题了,解题的过程就是学习、试错、再学习、掌握和巩固的过程,学习进度会大大加快。

环境准备

在Windows下安装,用官网上的rustup直接默认安装即可。

安装完成之后,就有了《The Rust Programming Language》这本书的离线HTML版本,直接用命令打开:

  1. rustup doc --book

还要会使用强大的包管理器:cargo

这个cargo好用的另人发指,建项目、编译、运行都用用它:

  1. cargo new euler1
  2. cd euler1
  3. cargo build
  4. cargo run

第一题

问题描述:

1000以内(不含1000)的所有被3或5整除的整数之和。

直接上答案:

  1. let mut sum = 0;
  2. for i in 1..1000 {
  3. if i % 3 == 0 || i % 5 == 0 {
  4. sum += i;
  5. }
  6. }
  7. println!("{}", sum);

mut关键字(mutable的缩写)是Rust的一大特色,所有变量默认为不可变的,如果想可变,需要mut关键字,否则在 sum += i 时会报编译错误。

println! 后面有一个叹号,表示这是一个宏,Rust里的宏也是非常非常强大!现在还不到了解的时候。

学过Python的列表推导(List Comprehension)语法的感觉这种题完全可以用一行语句搞定,Rust中需要用到filter()和sum()函数。

  1. // 为了阅读,分成多行
  2. println!(
  3. "{}",
  4. (1..1000).filter(|x| x % 3 == 0 || x % 5 == 0)
  5. .sum::<u32>()
  6. );

.. 这个语法糖表示一个范围,需要注意最后不包括1000,如果想包含1000,需要这样写:(1..=1000)

filter里面的|x|定义了一个闭包函数,关于闭包,又是一个复杂的主题。

sum::() 是一个范型函数,这种两个冒号的语法让我好不适应。

还可以用fold()函数,是这样写的:

  1. println!(
  2. "{}",
  3. (1..1000)
  4. .filter(|x| x % 3 == 0 || x % 5 == 0)
  5. .fold(0, |s, a| s + a)
  6. );

想把这些数全部打印出来:

  1. println!(
  2. "{:?}",
  3. (1..1000)
  4. .filter(|x| x % 3 == 0 || x % 5 == 0)
  5. .collect::<Vec<u32>>()
  6. );
  7. // [3, 5, 6, 9, 10, 12, ... 999]

第二题

问题描述:

400万之内所有偶数的斐波那契数字之和。

算法并不难,这里的数列以[1, 2]开始,后面每个数是前面2个数字之和:

  1. let mut fib = vec![1, 2];
  2. let mut i = 2; // 已经有2个元素
  3. let mut sum = 2;
  4. loop {
  5. let c = fib[i - 1] + fib[i - 2];
  6. if c >= 4_000_000 {
  7. break;
  8. }
  9. fib.push(c);
  10. if c % 2 == 0 {
  11. sum += c;
  12. }
  13. i += 1;
  14. }
  15. println!("{}", sum);

这里没有使用函数式编程,大量使用了mut,无限循环用loop语法。

rust中关于整数的表示提供了多种数据类型,默认的整数类型是i32,默认浮点类型是f64。

数字类型中比较有特点的是可以用'_'分隔符,让数字更容易读一些,还可以把u32, i64等类型作为后缀来指明类型。

let 赋值语句与其它语言也不一样,还可以改变其类型,这个特性为隐藏shadowing。

  1. let x = 500u16;
  2. let x = x + 1;
  3. let x = 4_000_000_u64;
  4. let x = "slb";

fib是一个向量,相当于其它语言里的数组、列表。vec! 宏可以进行初始化任务。

这一行:

  1. let mut fib = vec![1, 2];

与下面三行等价:

  1. let mut fib = Vec::new();
  2. fib.push(1);
  3. fib.push(2);

push()函数用于给列表增加一个元素。

还可以改进,利用rust的延迟评价特性,有起始值无终止值的无限序列可以用for语句搞定,原来的代码可以再精练一些,这种“2..”的语法在其它语言是无法想像的。

  1. let mut fib = vec![1, 2];
  2. let mut sum = 2;
  3. for i in 2.. {
  4. let c = fib[i - 1] + fib[i - 2];
  5. if c >= 4_000_000 {
  6. break;
  7. }
  8. fib.push(c);
  9. if c % 2 == 0 {
  10. sum += c;
  11. }
  12. }
  13. println!("{}", sum);

如果再使用函数式编程,还可以更精练一点:

  1. let mut fib = vec![1, 2];
  2. for i in 2.. {
  3. let c = fib[i - 1] + fib[i - 2];
  4. if c >= 4_000_000 { break; }
  5. fib.push(c);
  6. }
  7. println!("{}", fib.iter().filter(|&x| x % 2 == 0).sum::<u32>());

第三题

问题描述:

找出整数600851475143的最大素数因子。

素数就是只能被1和本身整除的数,首先定义一个函数is_prime(),用于判断是否为素数:

  1. fn is_prime(num: u64) -> bool {
  2. for i in 2..(num / 2 + 1) {
  3. if num % i == 0 {
  4. return false;
  5. }
  6. }
  7. true
  8. }

Rust是强类型语言,看到函数定义里的 -> bool,让我想起了Haskell的语法。

函数最后一行的true孤零零的,没有分号,让人感觉很奇怪。Rust是一个基于表达式的语言,一个语句块的最后是一个表达式,当然也可以用return true;

现在可以查找最大的素数因子了:

  1. let big_num = 600851475143;
  2. for i in (2..=big_num).rev() {
  3. if big_num % i == 0 && is_prime(i) {
  4. println!("{}", i);
  5. break;
  6. }
  7. }

程序编译没问题,但几分钟也运行不出来结果,试着把数字调小一点,比如:600851,不到1秒出来结果,看来程序的效率太差了,主要是需要大量的判断素数的运算量,需要优化。

尝试把大数进行素数因子分解,并且把素因子记录下来进行比较,效率得到大幅提升,不到1秒得出结果。

  1. let mut big_num = 600851475143;
  2. let mut max_prime_factor = 2;
  3. while big_num >= 2 {
  4. for i in 2..=big_num {
  5. if big_num % i == 0 && is_prime(i) {
  6. big_num /= i;
  7. if i > max_prime_factor {
  8. max_prime_factor = i;
  9. break;
  10. }
  11. }
  12. }
  13. }
  14. println!("{}", max_prime_factor);

第四题

问题描述:

求两个3位数之积最大的回文数。

所谓回文数,就是两边读都一样的数,比如:698896。

先写一个判断回文数的函数:

  1. fn is_palindromic(n: u64) -> bool {
  2. let s = n.to_string();
  3. s.chars().rev().collect::<String>() == s
  4. }

我把数字转换成字符串,再把字符串反序,如果与原字符串一样,则是回文数。

Rust中字符串的反序操作好奇怪,竟然不是s.rev(),我是google找到的那个代码片段。

剩下的逻辑并不复杂,用两重循环可以快速搞定。

  1. let mut max = 0;
  2. for x in 100..=999 {
  3. for y in 100..=999 {
  4. let prod = x * y;
  5. if is_palindromic(prod) && prod > max {
  6. max = prod;
  7. // println!("{} x {} = {}", x, y, prod);
  8. }
  9. }
  10. }
  11. println!("{}", max);

我一开始以为只要反序搜索就可以快速找到答案,但找到的数并不是最大,你能发现问题之所在吗?不过,从这个错误代码中,我学会了双重循环如何跳出外层循环的语法。真是没有白走的弯路。

  1. // 错误代码
  2. 'outer: for x in (100..=999).rev() {
  3. for y in (100..=999).rev() {
  4. let prod = x * y;
  5. if is_palindromic(prod) {
  6. println!("{} x {} = {}", x, y, prod);
  7. break 'outer;
  8. }
  9. }
  10. }

第五题

问题描述:

找出能够被1, 2, 3, ..., 20整除的最小整数。

代码逻辑很简单,一个一个尝试整除,找到后跳出最外层循环。

  1. let mut x = 2 * 3 * 5 * 7;
  2. 'outer: loop {
  3. for f in 2..=20 {
  4. if x % f != 0 {break;}
  5. if f == 20 {
  6. println!("{}", x);
  7. break 'outer;
  8. }
  9. }
  10. x += 2;
  11. }

如果你感觉程序运行效率不够高,可以用下面这个命令行运行,差别还是非常大的,感觉与C程序的效率相媲美:

  1. cargo run --release

第六题

问题描述:

求1到100自然数的“和的平方”与“平方和”的差。

用普通的过程式编程方法,这题太简单,但要尝试一下函数式编程思路,代码可以异常简洁。

  1. let sum_of_squares = (1..=100).map(|x| x*x).sum::<u32>();
  2. let sum = (1..=100).sum::<u32>();
  3. println!("{}", sum * sum - sum_of_squares);

另外还有一种使用fold()函数的写法,理解起来更困难一些:

  1. let sum_of_squares = (1..=100).fold(0, |s, n| s + n * n);
  2. let sum = (1..=100).fold(0, |s, n| s + n);
  3. println!("{}", sum * sum - sum_of_squares);

通过欧拉计划学Rust(第1~6题)的更多相关文章

  1. 通过欧拉计划学Rust编程(第54题)

    由于研究Libra等数字货币编程技术的需要,学习了一段时间的Rust编程,一不小心刷题上瘾. 刷完欧拉计划中的63道基础题,能学会Rust编程吗? "欧拉计划"的网址: https ...

  2. 通过欧拉计划学Rust编程(第500题)

    由于研究Libra等数字货币编程技术的需要,学习了一段时间的Rust编程,一不小心刷题上瘾. "欧拉计划"的网址: https://projecteuler.net 英文如果不过关 ...

  3. 用欧拉计划学Rust编程(第26题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  4. 用欧拉计划学Rust语言(第17~21题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  5. 用欧拉计划学Rust语言(第7~12题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  6. 通过欧拉计划学习Rust编程(第22~25题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  7. 用欧拉计划学习Rust编程(第13~16题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  8. 刷完欧拉计划中难度系数为5%的所有63道题,我学会了Rust中的哪些知识点?

    我为什么学Rust? 2019年6月18日,Facebook发布了数字货币Libra的技术白皮书,我也第一时间体验了一下它的智能合约编程语言MOVE,发现这个MOVE是用Rust编写的,看来想准确理解 ...

  9. 【欧拉计划4】Largest palindrome product

    欢迎访问我的新博客:http://www.milkcu.com/blog/ 原文地址:http://www.milkcu.com/blog/archives/1371281760.html 原创:[欧 ...

随机推荐

  1. Microsoft.Extensions.DependencyInjection 之一:解析实现

    [TOC] 前言 项目使用了 Microsoft.Extensions.DependencyInjection 2.x 版本,遇到第2次请求时非常高的内存占用情况,于是作了调查,本文对 3.0 版本仍 ...

  2. WPF 选择文件选择文件夹

    namespace Microsoft.Win32 选择文件: if (string.IsNullOrEmpty(folderInitialDirectory)) { folderInitialDir ...

  3. C 储存类与运算符

    储存类 参考链接:https://www.runoob.com/cprogramming/c-storage-classes.html 存储类定义 C 程序中变量/函数的范围(可见性)和生命周期 au ...

  4. C#解析JSON数组

    方式一 第一步:使用前,需下载:Newtonsoft.Json.dll 没有的,请到我百度云盘下载 链接:https://pan.baidu.com/s/1JBkee4qhtW7XOyYFiGOL2Q ...

  5. 将多个sass文件合并到一个文件中

    将多个sass文件合并到一个文件中 应用场景:制作angular npm包的时候,定义的一些全局样式,自定义主题色这类的情况下,多个scss文件会要合并成一个文件并写到dist文件里,发布到仓库中. ...

  6. FCC-学习笔记 Boo who

    FCC-学习笔记  Boo who 1>最近在学习和练习FCC的题目.这个真的比较的好,推荐给大家. 2>中文版的地址:https://www.freecodecamp.cn/;英文版的地 ...

  7. iOS开发之--iPhone X 适配:MJRefresh上拉加载适配

    问题如下图: 出现原因,phoneX系列手机下方多了34像素的工作区域,所以需要对x全系列手机坐下适配, 解决如下: self.tableView.mj_footer.ignoredScrollVie ...

  8. flink Periodic Watermarks 自定义周期性水印

    1.BoundedOutOfOrdernessGenerator /** * This generator generates watermarks assuming that elements ar ...

  9. tushare库:免费的python财经数据接口

    tushare官网以及在线文档http://tushare.org/ 安装    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tus ...

  10. 使用BurpSuite做中转代理时候出现Failed to connect to www.xxx.com:443 的时候可能原因

    1.可能是BurpSuite没有设置好代理,需要BurpSuite需要进行设置如下图: