Expression作参数,如何传递与使用?

99ANYc3cd6
预计阅读时长 21 分钟
位置: 首页 参数 正文

这个概念在不同的编程语言中有不同的体现,但其核心思想是:将一段代码(一个表达式)本身,而不是它的计算结果,传递给一个函数或方法,以便在函数内部执行这段代码。

这主要有两种实现方式,一种是函数式编程中的高阶函数,另一种是面向对象编程中的策略模式。


函数式编程中的“表达式”作为参数 (Lambda / 匿名函数)

这是最直接、最常见的方式,这里的“表达式”通常以 Lambda 表达式匿名函数 的形式出现,函数接受这个表达式后,通常会在某个时机去“调用”它。

核心思想

一个函数(我们称之为“高阶函数”)可以接受另一个函数作为参数,这个被传入的函数,就是我们所说的“表达式”的封装。

通俗比喻

想象一下你去餐厅点餐:

  • :顾客
  • 厨师:一个函数
  • 菜谱:一个具体的函数/表达式,告诉你如何做一道菜(加辣”、“不要香菜”)
  • 点餐过程:你把“菜谱”交给厨师,而不是自己做完菜再给厨师。

厨师拿到“菜谱”(函数)后,会在烹饪时执行它,你并没有提前把做好的菜(计算结果)给厨师。

示例

Python

Python 的 lambdadef 都可以创建这样的表达式。map, filter, sorted 等内置函数都是典型的高阶函数。

# 1. 使用 lambda 表达式
# 需求:将一个列表中的每个数字都乘以 2
numbers = [1, 2, 3, 4, 5]
# map() 函数接受一个函数和一个可迭代对象
# 第一个参数 lambda x: x * 2 就是一个“表达式”,它告诉 map() 如何处理每个元素
doubled_numbers = map(lambda x: x * 2, numbers)
print(list(doubled_numbers))  # 输出: [2, 4, 6, 8, 10]
# 2. 使用 def 定义的函数 (更清晰)
def multiply_by_two(x):
    return x * 2
doubled_numbers_v2 = map(multiply_by_two, numbers)
print(list(doubled_numbers_v2)) # 输出: [2, 4, 6, 8, 10]
# 3. 另一个例子:排序
# 需求:按照字符串的长度对列表进行排序
words = ["apple", "banana", "cherry", "date"]
# sorted() 的 key 参数接受一个函数,该函数返回用于比较的值
sorted_words = sorted(words, key=lambda s: len(s))
print(sorted_words) # 输出: ['date', 'apple', 'banana', 'cherry']

JavaScript

JavaScript 中的箭头函数是现代处理这个问题的标准方式。

// 1. 使用箭头函数
// 需求:过滤出数组中的偶数
const numbers = [1, 2, 3, 4, 5, 6];
// filter() 接受一个函数,该函数返回 true/false 来决定是否保留元素
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // 输出: [2, 4, 6]
// 2. 另一个例子:数组排序
const words = ["apple", "banana", "cherry", "date"];
// sort() 的 compareFunction 参数接受两个函数,并返回比较结果
const sortedWords = words.sort((a, b) => a.length - b.length);
console.log(sortedWords); // 输出: ['date', 'apple', 'banana', 'cherry']

Java (Java 8+)

Java 引入了 Lambda 表达式来简化这种操作。

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class ExpressionAsParameter {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        // 需求:过滤出大于 3 的数字
        // Predicate<T> 是一个函数式接口,代表一个接受 T 类型参数并返回 boolean 的表达式
        Predicate<Integer> isGreaterThanThree = num -> num > 3;
        numbers.stream()
               .filter(isGreaterThanThree) // 将表达式 (Predicate) 作为参数传递
               .forEach(System.out::println); // 输出: 4, 5, 6
    }
}

面向对象编程中的“表达式”作为参数 (策略模式)

在这种模式下,“表达式”被封装在一个对象中,这个对象通常实现了一个特定的接口(Comparator, Runnable),我们将这个对象作为参数传递给另一个方法,由该方法调用对象的接口方法。

核心思想

将算法或行为封装成独立的对象,使得它们可以相互替换,传递的不是一个函数,而是一个“知道如何做某事”的对象。

通俗比喻

继续用餐厅的比喻:

  • :顾客
  • 厨师:一个方法
  • 一个“调味师”对象:这个对象内部封装了“如何调味”的逻辑(川菜调味师”或“粤菜调味师”)。
  • 点餐过程:你告诉厨师,请“川菜调味师”来处理你的菜。

厨师并不关心调味师的具体逻辑,他只需要调用调味师的 applySeasoning() 方法即可。

示例

Java (Comparator 接口)

这是策略模式的经典案例。List.sort() 方法需要一个 Comparator 对象来决定排序规则。

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class Person {
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}
public class StrategyPatternExample {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));
        // 1. 按年龄排序 (使用匿名内部类,这是 Java 8 之前的 Lambda 替代品)
        // 整个 new Comparator<>() {...} 部分就是“表达式”的封装
        people.sort(new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return Integer.compare(p1.age, p2.age); // 这里的逻辑就是表达式
            }
        });
        System.out.println("按年龄排序: " + people);
        // 2. 按名字排序 (使用 Lambda 表达式,更简洁)
        // Lambda 表达式 `p1, p2 -> p1.name.compareTo(p2.name)` 就是那个“表达式”
        people.sort((p1, p2) -> p1.name.compareTo(p2.name));
        System.out.println("按名字排序: " + people);
    }
}

C# (委托和 Lambda)

C# 中委托是实现这一模式的天然工具,而 Lambda 是创建委托实例的简洁语法。

using System;
using System.Collections.Generic;
public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}
public class Program
{
    public static void Main()
    {
        var products = new List<Product>
        {
            new Product { Name = "Apple", Price = 1.2m },
            new Product { Name = "Banana", Price = 0.5m },
            new Product { Name = "Cherry", Price = 2.5m }
        };
        // Sort() 方法接受 Comparison<Product> 委托作为参数
        // Lambda 表达式 `p1, p2 => p1.Price.CompareTo(p2.Price)` 就是表达式
        products.Sort((p1, p2) => p1.Price.CompareTo(p2.Price));
        foreach (var p in products)
        {
            Console.WriteLine($"{p.Name}: {p.Price}");
        }
    }
}

总结与对比

特性 函数式 (Lambda/匿名函数) 面向对象 (策略模式)
本质 将一段可执行的代码块作为参数。 将一个封装了行为的对象作为参数。
形式 lambda x: x*2, num => num % 2 === 0 new Comparator() {...}, new MyStrategy()
优点 简洁、轻量级,适合一次性、简单的逻辑。 更灵活、可复用、可组合,适合复杂的、需要状态的逻辑。
适用场景 数据处理(map, filter, sort)、事件回调。 排序算法切换、验证规则、支付方式等需要替换算法的场景。
关系 现代语言中,两者界限越来越模糊。 Lambda 表达式常常是实现策略模式的一种更简洁的方式,Java 的 Comparator 可以接受一个 Lambda,它既是函数式也是策略思想的应用。

核心要点: 无论哪种形式,“将 expression 作为参数”的核心都是延迟执行行为抽象,它允许你将“做什么”(What to do)和“怎么做”(How to do)分离开来,让你的代码更加灵活、可读和可维护,这是现代编程中实现高内聚、低耦合设计的关键技术之一。

-- 展开阅读全文 --
头像
iPhone XS Max拆机超清图藏着哪些秘密?
« 上一篇 今天
Apogee Groove参数有哪些核心差异?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

最近发表

标签列表

目录[+]