ch13-00-functional-features.md commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是 函数式编程(functional programming)。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量 以供之后执行等等。 本章我们不会讨论函数式编程是或不是什么的问题,而是展示 Rust 的一些在功能上与其他被认为是函 数式语言类似的特性。 更具体的,我们将要涉及: • 闭包(Closures),一个可以储存在变量里的类似函数的结构 • 迭代器(Iterators),一种处理元素序列的方式 • 如何使用这些功能来改进第十二章的 I∕O 项目。 • 这两个功能的性能。(剧透警告:他们的速度超乎你的想象!) 还有其它受函数式风格影响的 Rust 功能,比如模式匹配和枚举,这些已经在其他章节中讲到过了。掌握 闭包和迭代器则是编写符合语言风格的高性能 Rust 代码的重要一环,所以我们将专门用一整章来讲解 他们。 闭包:可以捕获环境的匿名函数 ch13-01-closures.md commit 8acef6cfd40a36be60a3c62458d9f78e2427e190 Rust 的 闭包(closures)是可以保存进变量或作为参数传递给其他函数的匿名函数。可以在一个地方创 建闭包,然后在不同的上下文中执行闭包运算。不同于函数,闭包允许捕获调用者作用域中的值。我们 将展示闭包的这些功能如何复用代码和自定义行为。 使用闭包创建行为的抽象 让我们来看一个存储稍后要执行的闭包的示例。其间我们会讨论闭包的语法、类型推断和 trait。 考虑一下这个假定的场景:我们在一个通过 app 生成自定义健身计划的初创企业工作。其后端使用 Rust 编写,而生成健身计划的算法需要考虑很多不同的因素,比如用户的年龄、身体质量指数(Body Mass Index)、用户喜好、最近的健身活动和用户指定的强度系数。本例中实际的算法并不重要,重要的是这 个计算只花费几秒钟。我们只希望在需要时调用算法,并且只希望调用一次,这样就不会让用户等得太久。 这里将通过调用 simulated_expensive_calculation 函数来模拟调用假定的算法,如示例 13-1 所示,它 会打印出 calculating slowly … ,等待两秒,并接着返回传递给它的数字: 文件名: src∕main.rs use std::thread; use std::time::Duration; fn simulated_expensive_calculation(intensity: u32) -> u32 { println!(“calculating slowly…”); thread::sleep(Duration::from_secs(2)); intensity }

fn main() {}

示例 13-1:一个用来代替假定计算的函数,它大约会执行两秒钟 接下来,main 函数中将会包含本例的健身 app 中的重要部分。这代表当用户请求健身计划时 app 会调 用的代码。因为与 app 前端的交互与闭包的使用并不相关,所以我们将硬编码代表程序输入的值并打印 输出。 所需的输入有这些: • 一个来自用户的 intensity 数字,请求健身计划时指定,它代表用户喜好低强度还是高强度健身。 • 一个随机数,其会在健身计划中生成变化。 程序的输出将会是建议的锻炼计划。示例 13-2 展示了我们将要使用的 main 函数: 文件名: src∕main.rs

use std::thread;

use std::time::Duration;

fn simulated_expensive_calculation(intensity: u32) -> u32 {

println!(“calculating slowly…”);

thread::sleep(Duration::from_secs(2));

intensity

}

fn generate_workout(intensity: u32, random_number: u32) {}

fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } 示例 13-2:main 函数包含了用于 generate_workout 函数的模拟用户输入和模拟随机数输入 出于简单考虑这里硬编码了 simulated_user_specified_value 变量的值为 10 和 simulated_random_number 变量的值为 7;一个实际的程序会从 app 前端获取强度系数并使用 rand crate 来生成随机数,正如第二 章的猜猜看游戏所做的那样。main 函数使用模拟的输入值调用 generate_workout 函数: 现在有了执行上下文,让我们编写算法。示例 13-3 中的 generate_workout 函数包含本例中我们最关 心的 app 业务逻辑。本例中余下的代码修改都将在这个函数中进行: 文件名: src∕main.rs

use std::thread;

use std::time::Duration;

fn simulated_expensive_calculation(intensity: u32) -> u32 {

println!(“calculating slowly…”);

thread::sleep(Duration::from_secs(2));

intensity

}

fn generate_workout(intensity: u32, random_number: u32) { if intensity < 25 { println!( “Today, do {} pushups!”, simulated_expensive_calculation(intensity) ); println!( “Next, do {} situps!”, simulated_expensive_calculation(intensity) ); } else { if random_number == 3 { println!(“Take a break today! Remember to stay hydrated!”); } else { println!( “Today, run for {} minutes!”, simulated_expensive_calculation(intensity) ); } } }

fn main() {

let simulated_user_specified_value = 10;

let simulated_random_number = 7;

generate_workout(simulated_user_specified_value, simulated_random_number);

}

示例 13-3:程序的业务逻辑,它根据输入并调用 simulated_expensive_calculation 函数来打印出健身 计划 示例 13-3 中的代码有多处调用了慢计算函数 simulated_expensive_calculation 。第一个 if 块调用了 simulated_expensive_calculation 两次,else 中的 if 没有调用它,而第二个 else 中的代码调用了它一 次。 generate_workout 函数的期望行为是首先检查用户需要低强度(由小于 25 的系数表示)锻炼还是高强 度(25 或以上)锻炼。 低强度锻炼计划会根据由 simulated_expensive_calculation 函数所模拟的复杂算法建议一定数量的俯 卧撑和仰卧起坐。 如果用户需要高强度锻炼,这里有一些额外的逻辑:如果 app 生成的随机数刚好是 3,app 相反会建议 用户稍做休息并补充水分。如果不是,则用户会从复杂算法中得到数分钟跑步的高强度锻炼计划。 现在这份代码能够应对我们的需求了,但数据科学部门的同学告知我们将来会对调用 simulated_expensive_calculation 的方式做出一些改变。为了在要做这些改动的时候简化更新步骤,我们将重构代码来让它只调用 simulated_expensive_calculation 一次。同时还希望去掉目前多余的连续两次函数调用,并不希望在计 算过程中增加任何其他此函数的调用。也就是说,我们不希望在完全无需其结果的情况调用函数,不过 仍然希望只调用函数一次。

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: