青少年编程与数学 02-019 Rust 编程基础 10课题、函数、闭包和迭代器

发布于:2025-05-14 ⋅ 阅读:(15) ⋅ 点赞:(0)

课题摘要:
在 Rust 中,函数是程序的基本构建块,用于封装可重用的代码逻辑。闭包(Closure)是一种特殊的匿名函数,它可以捕获和存储其定义环境中的变量。迭代器(Iterator)是一种用于遍历集合(如数组、向量、哈希表等)的抽象接口。

关键词:函数、闭包、迭代器


一、函数

在 Rust 中,函数是程序的基本构建块,用于封装可重用的代码逻辑。函数可以接受参数、执行操作并返回值。Rust 的函数设计既灵活又安全,支持多种参数类型、返回值类型以及高级特性(如闭包和泛型)。以下是对 Rust 中函数的详细解析。

1. 函数的基本定义

在 Rust 中,函数使用 fn 关键字定义,后面跟函数名、参数列表和返回值类型(可选)。函数体用大括号 {} 包裹。

基本语法

fn 函数名(参数列表) -> 返回类型 {
    // 函数体
}
  • 函数名:标识函数的名称。
  • 参数列表:函数的输入参数,可以有多个参数,用逗号分隔。每个参数需要指定类型。
  • 返回类型:函数返回值的类型,用 -> 指定。如果函数没有返回值,则可以省略 -> 返回类型
  • 函数体:函数的具体实现逻辑。

示例

以下是一个简单的函数,用于计算两个整数的和:

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(3, 5);
    println!("The result is {}", result); // 输出:The result is 8
}

在这个例子中:

  • add 是函数名。
  • a: i32, b: i32 是参数列表,指定了两个整数参数。
  • -> i32 表示函数返回一个 i32 类型的值。
  • 函数体中直接返回了 a + b 的结果。

2. 函数的返回值

Rust 中的函数可以返回值,也可以不返回值。如果函数没有返回值,则其返回类型为 (),即空元组类型。

返回值示例

fn main() {
    let result = add(3, 5);
    println!("The result is {}", result); // 输出:The result is 8

    let message = greet("Kimi");
    println!("{}", message); // 输出:Hello, Kimi!
}

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

不返回值的函数

如果函数没有返回值,可以省略 -> 返回类型

fn print_sum(a: i32, b: i32) {
    println!("The sum is {}", a + b);
}

fn main() {
    print_sum(3, 5); // 输出:The sum is 8
}

3. 参数传递

Rust 中的参数传递有以下几种方式:

  • 值传递:将参数的值传递给函数,函数内部对参数的修改不会影响外部变量。
  • 引用传递:将参数的引用传递给函数,函数内部可以通过引用修改外部变量。
  • 可变引用传递:将可变引用传递给函数,允许函数内部修改外部变量。

值传递示例

fn main() {
    let a = 5;
    let b = 3;

    let result = add(a, b);
    println!("The result is {}", result); // 输出:The result is 8
    println!("a = {}, b = {}", a, b); // 输出:a = 5, b = 3
}

fn add(mut x: i32, y: i32) -> i32 {
    x += y; // 修改 x 的值
    x
}

在这个例子中,ab 的值被传递给 add 函数,函数内部对 x 的修改不影响外部的 a

引用传递示例

fn main() {
    let a = 5;

    print_value(&a); // 输出:The value is 5
}

fn print_value(value: &i32) {
    println!("The value is {}", value);
}

在这个例子中,a 的引用被传递给 print_value 函数,函数内部通过引用访问 a 的值。

可变引用传递示例

fn main() {
    let mut a = 5;

    increment(&mut a); // 修改 a 的值
    println!("a = {}", a); // 输出:a = 6
}

fn increment(value: &mut i32) {
    *value += 1; // 通过可变引用修改外部变量
}

在这个例子中,a 的可变引用被传递给 increment 函数,函数内部通过解引用 *value 修改了外部变量 a 的值。

4. 函数的高级特性

4.1 泛型函数

Rust 支持泛型函数,允许函数在定义时不指定具体的类型,而是在调用时根据参数类型推断。

示例
fn main() {
    let a = 5;
    let b = 3.14;

    println!("The maximum is {}", max(a, b)); // 输出:The maximum is 5
}

fn max<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a > b {
        a
    } else {
        b
    }
}

在这个例子中:

  • max 是一个泛型函数,T 是一个类型参数。
  • T: PartialOrd + Copy 是类型约束,表示 T 必须实现 PartialOrdCopy 特性。
  • 函数内部通过 if a > b 比较两个值,并返回较大的值。

4.2 高阶函数

高阶函数是接受函数或闭包作为参数的函数。Rust 中的高阶函数可以用于实现函数式编程风格。

示例
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    let sum = numbers.iter().fold(0, |acc, x| acc + x); // 使用 fold 函数
    println!("The sum is {}", sum); // 输出:The sum is 15
}

在这个例子中,fold 是一个高阶函数,它接受一个初始值和一个闭包,用于对迭代器中的元素进行累加。

5. 函数的调用约定

Rust 支持多种函数调用约定,包括默认的调用约定和外部调用约定(如 C 调用约定)。

默认调用约定

Rust 的默认调用约定是 Rust,适用于 Rust 内部的函数调用。

外部调用约定

如果需要与其他语言(如 C)交互,可以使用外部调用约定。例如:

extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

在这个例子中,extern "C" 表示使用 C 的调用约定。

6. 小结

Rust 中的函数是程序的核心组件,具有以下特点:

  • 灵活性:支持多种参数类型、返回值类型和高级特性(如泛型、闭包和高阶函数)。
  • 安全性:通过所有权和借用规则,确保函数调用的安全性。
  • 可重用性:函数可以封装逻辑,便于代码复用。

通过合理使用函数,可以提高代码的可读性、可维护性和可扩展性。

二、闭包

在 Rust 中,闭包(Closure)是一种特殊的匿名函数,它可以捕获和存储其定义环境中的变量。闭包在 Rust 中非常灵活且强大,常用于函数式编程风格,尤其是在需要将函数作为参数传递或返回函数时。以下是对 Rust 中闭包的详细解析。

1. 闭包的基本概念

闭包是一种可以捕获其定义环境中的变量的匿名函数。它可以在定义时捕获外部变量,并在后续调用中使用这些变量。

基本语法

闭包的语法如下:

|参数列表| -> 返回类型 { 代码块 }
  • 参数列表:闭包的输入参数,用竖线 | 包裹。
  • 返回类型:可选,如果闭包有返回值,可以用 -> 指定返回类型。
  • 代码块:闭包的主体,用大括号 {} 包裹。

示例

以下是一个简单的闭包示例,用于计算两个整数的和:

fn main() {
    let add = |a: i32, b: i32| -> i32 { a + b }; // 定义闭包
    let result = add(3, 5);
    println!("The result is {}", result); // 输出:The result is 8
}

在这个例子中:

  • |a: i32, b: i32| -> i32 { a + b } 是一个闭包,它接受两个整数参数并返回它们的和。
  • 闭包被赋值给变量 add,并通过变量调用。

2. 闭包的类型

Rust 中的闭包根据其捕获环境变量的方式分为三种类型:

  • FnOnce:闭包可以被调用一次,因为它可能需要获取捕获的变量的所有权。
  • FnMut:闭包可以被多次调用,但每次调用可能需要修改捕获的变量。
  • Fn:闭包可以被多次调用,且不会修改捕获的变量。

2.1 FnOnce

FnOnce 是最基本的闭包类型,它表示闭包可以被调用一次。如果闭包获取了捕获变量的所有权,则该闭包属于 FnOnce 类型。

示例
fn main() {
    let name = String::from("Kimi");

    let print_name = || {
        println!("The name is {}", name);
    };

    print_name(); // 输出:The name is Kimi
    // print_name(); // 错误:`print_name` 被移动了,无法再次调用
}

在这个例子中,闭包 print_name 捕获了变量 name 的所有权,因此它属于 FnOnce 类型。调用一次后,闭包被移动,无法再次调用。

2.2 FnMut

FnMut 表示闭包可以被多次调用,但每次调用可能需要修改捕获的变量。如果闭包捕获了变量的可变引用,则该闭包属于 FnMut 类型。

示例
fn main() {
    let mut counter = 0;

    let mut increment = || {
        counter += 1;
        println!("The counter is {}", counter);
    };

    increment(); // 输出:The counter is 1
    increment(); // 输出:The counter is 2
}

在这个例子中,闭包 increment 捕获了变量 counter 的可变引用,因此它属于 FnMut 类型。每次调用闭包时,counter 的值都会被修改。

2.3 Fn

Fn 表示闭包可以被多次调用,且不会修改捕获的变量。如果闭包捕获了变量的不可变引用,则该闭包属于 Fn 类型。

示例
fn main() {
    let name = String::from("Kimi");

    let print_name = || {
        println!("The name is {}", name);
    };

    print_name(); // 输出:The name is Kimi
    print_name(); // 输出:The name is Kimi
}

在这个例子中,闭包 print_name 捕获了变量 name 的不可变引用,因此它属于 Fn 类型。可以多次调用闭包,而不会修改捕获的变量。

3. 闭包的捕获方式

闭包捕获环境变量的方式取决于变量的使用方式:

  • 如果闭包需要修改捕获的变量,则捕获变量的可变引用。
  • 如果闭包需要多次使用捕获的变量,则捕获变量的不可变引用。
  • 如果闭包只需要使用一次捕获的变量,则捕获变量的所有权。

示例

fn main() {
    let mut count = 0;

    {
        let mut increment = || {
            count += 1;
        };
        increment();
    }

    println!("The count is {}", count); // 输出:The count is 1
}

在这个例子中,闭包 increment 捕获了变量 count 的可变引用,因此可以修改 count 的值。

4. 闭包的使用场景

闭包在 Rust 中非常灵活,常用于以下场景:

  • 作为函数参数:将闭包传递给函数,实现回调、迭代器操作等。
  • 作为函数返回值:从函数中返回闭包,实现延迟计算或封装逻辑。
  • 实现函数式编程风格:使用闭包实现 mapfilterfold 等高阶函数。

4.1 作为函数参数

闭包常用于函数式编程中的高阶函数。例如,mapfilter 函数接受闭包作为参数,对集合中的元素进行操作。

示例
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
    println!("Doubled numbers: {:?}", doubled); // 输出:Doubled numbers: [2, 4, 6, 8, 10]

    let even: Vec<i32> = numbers.into_iter().filter(|x| x % 2 == 0).collect();
    println!("Even numbers: {:?}", even); // 输出:Even numbers: [2, 4]
}

在这个例子中:

  • map(|x| x * 2) 使用闭包对每个元素进行加倍操作。
  • filter(|x| x % 2 == 0) 使用闭包过滤出偶数。

4.2 作为函数返回值

闭包可以作为函数的返回值,实现延迟计算或封装逻辑。

示例
fn main() {
    let add = create_adder(5);
    println!("The result is {}", add(3)); // 输出:The result is 8
}

fn create_adder(base: i32) -> impl Fn(i32) -> i32 {
    move |x| x + base
}

在这个例子中:

  • create_adder 函数返回一个闭包,该闭包捕获了参数 base 的所有权。
  • 返回的闭包可以被调用,实现延迟计算。

4.3 实现函数式编程风格

闭包是实现函数式编程风格的关键工具。例如,使用闭包可以实现 fold 函数,对集合中的元素进行累加。

示例
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    let sum = numbers.iter().fold(0, |acc, x| acc + x);
    println!("The sum is {}", sum); // 输出:The sum is 15
}

在这个例子中,fold 函数接受一个初始值和一个闭包,对集合中的元素进行累加。

5. 闭包的性能

闭包在 Rust 中是高效的,因为它们可以捕获环境变量,避免了不必要的数据传递。闭包的性能通常与普通函数相当,但在某些情况下,闭包可以更灵活地优化性能。

示例

fn main() {
    let mut numbers = vec![1, 2, 3, 4, 5];

    numbers.sort_by(|a, b| b.cmp(a)); // 使用闭包进行排序
    println!("Sorted numbers: {:?}", numbers); // 输出:Sorted numbers: [5, 4, 3, 2, 1]
}

在这个例子中,sort_by 函数接受一个闭包,用于定义排序的规则。闭包的使用使得代码更加简洁和高效。

6. 小结

闭包是 Rust 中一种非常强大和灵活的工具,具有以下特点:

  • 匿名函数:闭包没有名称,可以直接定义和使用。
  • 捕获环境变量:闭包可以捕获其定义环境中的变量,并在后续调用中使用这些变量。
  • 多种类型:闭包根据捕获变量的方式分为 FnOnceFnMutFn 三种类型。
  • 高效和灵活:闭包可以用于实现函数式编程风格,优化代码的可读性和性能。

通过合理使用闭包,可以实现复杂的逻辑控制,同时保持代码的简洁和高效。

三、迭代器

在 Rust 中,迭代器(Iterator)是一种用于遍历集合(如数组、向量、哈希表等)的抽象接口。迭代器提供了一种统一的方式来逐个访问集合中的元素,而无需暴露集合的内部表示。Rust 的迭代器设计非常灵活且功能强大,支持惰性求值、链式操作和多种迭代器适配器,使得数据处理更加高效和简洁。

1. 迭代器的基本概念

迭代器是一种可以逐个访问集合中元素的接口。在 Rust 中,迭代器通常实现了 Iterator 特性(trait),该特性定义了两个主要方法:

  • next():返回集合中的下一个元素,如果集合中没有更多元素,则返回 None
  • size_hint():返回一个元组 (lower_bound, upper_bound),表示迭代器可能返回的元素数量的范围。

示例

以下是一个简单的迭代器示例,用于遍历一个向量中的元素:

fn main() {
    let vec = vec![1, 2, 3, 4, 5];

    let mut iter = vec.into_iter(); // 创建迭代器

    while let Some(value) = iter.next() { // 使用 next() 方法逐个访问元素
        println!("{}", value);
    }
}

在这个例子中:

  • vec.into_iter() 创建了一个迭代器,用于遍历向量 vec 中的元素。
  • iter.next() 返回集合中的下一个元素,如果集合中没有更多元素,则返回 None
  • 使用 while let 语法逐个访问元素,直到迭代器耗尽。

2. 创建迭代器

Rust 提供了多种方式来创建迭代器:

  • 使用 iter() 方法:创建一个不可变引用迭代器,不会获取集合的所有权。
  • 使用 into_iter() 方法:创建一个获取集合所有权的迭代器。
  • 使用 iter_mut() 方法:创建一个可变引用迭代器,允许修改集合中的元素。

示例

fn main() {
    let vec = vec![1, 2, 3, 4, 5];

    // 创建不可变引用迭代器
    let iter = vec.iter();
    for value in iter {
        println!("{}", value);
    }

    // 创建获取所有权的迭代器
    let vec2 = vec![1, 2, 3, 4, 5];
    let into_iter = vec2.into_iter();
    for value in into_iter {
        println!("{}", value);
    }

    // 创建可变引用迭代器
    let mut vec3 = vec![1, 2, 3, 4, 5];
    let mut iter_mut = vec3.iter_mut();
    for value in iter_mut {
        *value += 1; // 修改元素
    }
    println!("{:?}", vec3); // 输出:[2, 3, 4, 5, 6]
}

3. 迭代器的惰性求值

Rust 的迭代器是惰性求值的,这意味着迭代器的元素只有在需要时才会被计算。这种特性使得迭代器可以高效地处理大型集合,甚至可以处理无限序列。

示例

以下是一个无限序列的示例:

fn main() {
    let numbers = 0..; // 创建一个无限序列
    let mut iter = numbers.skip(10).take(5); // 跳过前 10 个元素,取接下来的 5 个元素

    while let Some(value) = iter.next() {
        println!("{}", value); // 输出:10, 11, 12, 13, 14
    }
}

在这个例子中:

  • 0.. 创建了一个无限序列。
  • skip(10) 跳过前 10 个元素。
  • take(5) 取接下来的 5 个元素。
  • iter.next() 按需计算并返回元素。

4. 迭代器适配器

Rust 提供了许多迭代器适配器(Iterator Adapters),用于对迭代器进行转换和组合。这些适配器可以实现复杂的操作,而无需手动编写循环。

常见的迭代器适配器

  • map:对迭代器中的每个元素应用一个函数。
  • filter:根据条件过滤迭代器中的元素。
  • fold:对迭代器中的元素进行累加或其他累积操作。
  • collect:将迭代器中的元素收集到一个集合中。
  • filter_map:结合 filtermap 的功能。
  • flat_map:对迭代器中的每个元素应用一个函数,该函数返回一个迭代器,然后将所有迭代器的元素展平。
  • enumerate:为迭代器中的每个元素添加索引。
  • zip:将两个迭代器组合成一个迭代器。

示例

以下是一个使用多种迭代器适配器的示例:

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 使用 map 和 filter
    let result: Vec<i32> = numbers
        .iter()
        .map(|x| x * 2) // 对每个元素乘以 2
        .filter(|x| x % 3 != 0) // 过滤掉能被 3 整除的元素
        .collect(); // 收集到一个向量中

    println!("{:?}", result); // 输出:[2, 4, 8, 10]

    // 使用 fold 计算累加和
    let sum = numbers.iter().fold(0, |acc, x| acc + x);
    println!("The sum is {}", sum); // 输出:The sum is 15

    // 使用 enumerate 添加索引
    for (index, value) in numbers.iter().enumerate() {
        println!("Element at index {}: {}", index, value);
    }

    // 使用 zip 组合两个迭代器
    let vec1 = vec![1, 2, 3];
    let vec2 = vec!['a', 'b', 'c'];
    for (num, ch) in vec1.iter().zip(vec2.iter()) {
        println!("Number: {}, Character: {}", num, ch);
    }
}

5. 迭代器的链式操作

由于迭代器是惰性求值的,多个迭代器适配器可以链式调用,从而实现复杂的操作。链式操作不仅代码简洁,而且性能高效,因为中间结果不会被显式存储。

示例

以下是一个链式操作的示例:

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 链式操作:map -> filter -> collect
    let result: Vec<i32> = numbers
        .iter()
        .map(|x| x * 2) // 对每个元素乘以 2
        .filter(|x| x % 3 != 0) // 过滤掉能被 3 整除的元素
        .collect(); // 收集到一个向量中

    println!("{:?}", result); // 输出:[2, 4, 8, 10]
}

在这个例子中:

  • map(|x| x * 2) 对每个元素乘以 2。
  • filter(|x| x % 3 != 0) 过滤掉能被 3 整除的元素。
  • collect() 将结果收集到一个向量中。

6. 迭代器的性能

由于迭代器是惰性求值的,它们在处理大型数据集时非常高效。每次调用 next() 方法时,迭代器只会计算下一个元素,而不会提前计算所有元素。此外,链式操作避免了中间结果的存储,进一步提高了性能。

示例

以下是一个性能优化的示例:

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 使用迭代器的链式操作
    let sum: i32 = numbers
        .iter()
        .map(|x| x * 2) // 对每个元素乘以 2
        .filter(|x| x % 3 != 0) // 过滤掉能被 3 整除的元素
        .sum(); // 计算总和

    println!("The sum is {}", sum); // 输出:The sum is 24
}

在这个例子中:

  • map(|x| x * 2) 对每个元素乘以 2。
  • filter(|x| x % 3 != 0) 过滤掉能被 3 整除的元素。
  • sum() 计算总和,直接返回结果,避免了中间结果的存储。

7. 自定义迭代器

在 Rust 中,可以通过实现 Iterator 特性来创建自定义迭代器。这允许开发者定义自己的迭代逻辑,从而实现更复杂的数据处理。

示例

以下是一个自定义迭代器的示例,用于生成斐波那契数列:

struct Fibonacci {
    current: u32,
    next: u32,
}

impl Iterator for Fibonacci {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        let new_next = self.current + self.next;
        let new_current = self.next;

        self.current = new_current;
        self.next = new_next;

        Some(self.current)
    }
}

fn main() {
    let fib = Fibonacci { current: 0, next: 1 };

    for value in fib.take(10) {
        println!("{}", value);
    }
}

在这个例子中:

  • Fibonacci 是一个自定义迭代器,用于生成斐波那契数列。
  • 实现了 Iterator 特性,定义了 next 方法,用于生成下一个斐波那契数。
  • 使用 take(10) 限制生成的斐波那契数的数量。

8. 小结

Rust 的迭代器是一种非常强大和灵活的工具,具有以下特点:

  • 惰性求值:迭代器的元素只有在需要时才会被计算,这使得迭代器可以高效地处理大型数据集。
  • 链式操作:多个迭代器适配器可以链式调用,实现复杂的操作,同时保持代码简洁。
  • 多种适配器:Rust 提供了丰富的迭代器适配器,如 mapfilterfoldcollect 等,用于实现各种数据处理逻辑。
  • 性能高效:迭代器避免了中间结果的存储,进一步提高了性能。
  • 自定义迭代器:开发者可以通过实现 Iterator 特性来创建自定义迭代器,实现更复杂的数据处理逻辑。

通过合理使用迭代器,可以实现高效、简洁且可读性强的数据处理逻辑。

四、综合示例

以下是一个综合展示 Rust 中函数、闭包和迭代器用法的示例程序。这个程序将实现以下功能:

  1. 定义一个函数,计算两个整数的和。
  2. 使用闭包对一个整数向量中的每个元素进行加倍操作。
  3. 使用迭代器适配器对处理后的向量进行过滤和累加操作。
fn main() {
    // 1. 使用函数计算两个整数的和
    let a = 5; // i32
    let b = 3; // i32
    let sum = add(a, b);
    println!("The sum of {} and {} is {}", a, b, sum);

    // 2. 使用闭包对向量中的每个元素进行加倍操作
    let numbers = [1, 2, 3, 4, 5];
    let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
    println!("Doubled numbers: {:?}", doubled);

    // 3. 使用迭代器适配器过滤出偶数并计算总和
    let even_sum: i32 = doubled.iter().filter(|x| *x % 2 == 0).sum();
    println!("The sum of even numbers is {}", even_sum);
}

// 定义一个函数,计算两个整数的和
fn add(a: i32, b: i32) -> i32 {
    a + b
}

代码说明

  1. 函数的使用

    • 定义了一个名为 add 的函数,接受两个整数参数并返回它们的和。
    • main 函数中调用 add 函数,计算 ab 的和,并打印结果。
  2. 闭包的使用

    • 使用 map 迭代器适配器和闭包对 numbers 向量中的每个元素进行加倍操作。
    • 闭包 |x| x * 2 对每个元素 x 进行加倍处理。
    • 使用 collect 方法将处理后的结果收集到一个新的向量 doubled 中。
  3. 迭代器的使用

    • 使用 filter 迭代器适配器和闭包过滤出 doubled 向量中的偶数。
    • 使用 sum 方法对过滤后的偶数进行累加操作,并打印结果。

输出结果

运行上述程序后,输出如下:

The sum of 5 and 3 is 8
Doubled numbers: [2, 4, 6, 8, 10]
The sum of even numbers is 30

代码的正确性

  • 函数 add 正确实现了两个整数的加法。
  • 闭包 |x| x * 2 正确地对向量中的每个元素进行了加倍操作。
  • 迭代器适配器 mapfiltersum 的链式调用正确地实现了对数据的处理和累加操作。

总结

在 Rust 中,函数、闭包和迭代器是实现逻辑和数据处理的核心工具。函数是代码的基本构建块,使用 fn 定义,可以接受参数并返回值,支持泛型和多种调用约定,用于封装逻辑。闭包是匿名函数,可以捕获环境变量,分为 FnOnce(调用一次)、FnMut(可变调用)和 Fn(多次调用),常用于高阶函数和延迟计算。迭代器是用于逐个访问集合元素的接口,惰性求值,支持多种适配器(如 mapfilterfold),可高效处理数据,支持自定义实现。三者结合使用,可实现灵活、高效且安全的编程范式,是 Rust 函数式编程风格的重要体现。