Skip to content

MATLAB - 映射函数

一个常见的编程任务是对数组中的每个元素应用特定的操作或函数。虽然许多语言使用 map 函数来完成此任务,但 MATLAB 鼓励一种更高效、更易读的方法,称为向量化(vectorization)。在向量化不可行的情况下,MATLAB 提供了 arrayfun 和 cellfun 函数。

向量化是将基于循环、面向标量的代码重写为使用 MATLAB 的矩阵和向量运算的过程。它更快、更符合 MATLAB 习惯,因为 MATLAB 对数组操作进行了高度优化。你应该始终优先尝试使用向量化。

假设你有一个数字数组,并想对每个数字求平方。

% 慢速的、基于循环的方法(如果可能请避免)
input_array = [1, 2, 3, 4, 5];
output_array = zeros(size(input_array)); % 预分配以提高性能
for i = 1:length(input_array)
output_array(i) = input_array(i)^2;
end
% 快速的、向量化方法(最佳方法)
input_array = [1, 2, 3, 4, 5];
output_array_vectorized = input_array.^2; % .^ 运算符是逐元素幂运算
disp(output_array_vectorized);

向量化版本更简洁、更精炼,并且显著更快,特别是对于大型数组。输出为:

1 4 9 16 25

对数值数组使用 arrayfun(必要时)

Section titled “对数值数组使用 arrayfun(必要时)”

当你想应用的函数无法轻易向量化时,你应该使用 arrayfun。arrayfun 将函数应用于数组的每个元素,但它通常只是 for 循环的一种更简洁的语法,并且不提供与真正向量化相同的性能优势。

B = arrayfun(func, A)
  • B: 输出数组。
  • func: 你想要应用的函数的句柄。这通常是用 @ 创建的匿名函数。
  • A: 输入数组。

示例:应用自定义的、非向量化函数

Section titled “示例:应用自定义的、非向量化函数”

想象你有一个复杂的自定义函数,它只对标量输入操作。

% 让我们定义一个非向量化的自定义函数
my_complex_func = @(x) sin(x) + x^2 - log(x);
% 数字数组
input_array = [1, 2, 3, 4, 5];
% 使用 arrayfun 将函数应用于每个元素
output_array = arrayfun(my_complex_func, input_array);
disp(output_array);

虽然这可行,但一个更好的长期解决方案是重写 my_complex_func 以使其向量化:

% 函数的向量化版本
my_vectorized_func = @(x) sin(x) + x.^2 - log(x);
output_better = my_vectorized_func(input_array); % 这更快!

元胞数组的每个单元格可以包含不同类型和大小的数据,因此无法进行向量化。对于这些情况,cellfun 是适当的工具。

B = cellfun(func, C)

在现代 MATLAB 中,你应该使用 string 数组来处理文本。但如果你有一个遗留的字符向量 cell 数组,那么 cellfun 是完美的。

% 创建一个字符向量元胞数组
input_cell_array = {'apple', 'banana', 'cherry'};
% 使用 cellfun 对每个元素应用 length 函数
% 注意:在现代 MATLAB 中,更推荐对字符串数组使用 strlength()
lengths = cellfun(@length, input_cell_array);
disp(lengths);

结果是长度数组:

5 6 6

示例 2:求单元格中每个数值数组的和

Section titled “示例 2:求单元格中每个数值数组的和”

如果每个单元格包含不同大小的数值数组,cellfun 可以处理它们。

% 创建一个数值数组的元胞数组
numeric_cells = {[1, 2, 3], [4, 5], [6, 7, 8, 9]};
% 定义一个函数句柄来计算和
sum_function = @(arr) sum(arr);
% 使用 cellfun 应用求和函数
output_sums = cellfun(sum_function, numeric_cells);
disp(output_sums);

结果是每个单元格内容的和:

6 9 30
  1. 优先向量化:始终尝试使用逐元素运算符(.*、./、.^)和向量化函数(sin、log、sum 等),以获得最佳性能和可读性。
  2. 谨慎使用 arrayfun:仅当函数无法向量化时,才将 arrayfun 用于数值数组。
  3. 对元胞数组使用 cellfun:这是处理元胞数组异构内容的预期且高效的方法。
  4. 'UniformOutput', false:如果你通过 cellfun 或 arrayfun 应用的函数返回不同类型或大小的值,则必须指定此名称-值对。结果将是一个元胞数组。示例:sizes = cellfun(@size, numeric_cells, 'UniformOutput', false);