一般来说,函数在设计时就应该考虑矩阵参数,并以向量化的方式处理整个矩阵运算。然而,由于各种原因,有时以这种方式编写函数显得困难甚至不可能。针对这些情况,Octave 提供了将函数应用于数组、元胞数组或结构体的每个元素的工具。
B = arrayfun (fcn, A) ¶B = arrayfun (fcn, A1, A2, …) ¶[B1, B2, …] = arrayfun (fcn, A, …) ¶B = arrayfun (…, "UniformOutput", val) ¶B = arrayfun (…, "ErrorHandler", errfcn) ¶对数组的每个元素执行一个函数。
这对于不接受数组参数的函数非常有用。如果函数确实接受数组参数,则最好直接调用该函数。
第一个输入参数 fcn 可以是字符串、函数句柄、内联函数或匿名函数。输入参数 A 可以是逻辑数组、数值数组、字符串数组、结构体数组或元胞数组。arrayfun 将 A 的所有元素逐个传递给函数 fcn 并收集结果。其等效的伪代码如下:
cls = class (fcn (A(1)); B = zeros (size (A), cls); for i = 1:numel (A) B(i) = fcn (A(i)) endfor
命名函数还可以接受两个以上的输入参数,额外的输入参数以第三个输入参数 A2、第四个输入参数 A2 等形式给出,... 如果给定了多个数组输入参数,则所有输入参数的大小必须相同。例如:
arrayfun (@atan2, [1, 0], [0, 1])
⇒ [ 1.57080 0.00000 ]
如果在字符串输入参数 "UniformOutput" 之后的参数 val 被设置为 true(默认值),则命名函数 fcn 必须返回单个元素,该元素将被拼接成返回值,且返回值的类型为矩阵。否则,如果该参数设置为 false,则输出将被拼接成元胞数组。例如:
arrayfun (@(x,y) x:y, "abc", "def", "UniformOutput", false)
⇒
{
[1,1] = abcd
[1,2] = bcde
[1,3] = cdef
}
如果给定了多个输出参数,则命名函数必须返回对应数量的返回值。例如:
[A, B, C] = arrayfun (@find, [10; 0], "UniformOutput", false)
⇒
A =
{
[1,1] = 1
[2,1] = [](0x0)
}
B =
{
[1,1] = 1
[2,1] = [](0x0)
}
C =
{
[1,1] = 10
[2,1] = [](0x0)
}
如果在字符串输入参数 "ErrorHandler" 之后的参数 errfcn 是另一个字符串、函数句柄、内联函数或匿名函数,则 errfcn 定义了当 fcn 产生错误时调用的函数。该函数的定义形式必须为:
function [...] = errfcn (s, ...)
其中,相对于 fcn,errfcn 有一个额外的输入参数 s。这是一个结构体,包含 "identifier"、"message" 和 "index" 三个字段,分别表示错误标识符、错误消息以及导致错误的数组元素的索引。errfcn 的输出参数大小必须与 fcn 的输出参数大小相同,否则将引发真正的错误。例如:
function y = ferr (s, x), y = "MyString"; endfunction
arrayfun (@str2num, [1234],
"UniformOutput", false, "ErrorHandler", @ferr)
⇒
{
[1,1] = MyString
}
y = spfun (f, S) ¶计算 f (S) 中 S 的非零元素。
输入函数 f 仅应用于输入矩阵 S 的非零元素,S 通常为稀疏矩阵。函数 f 可以作为字符串、函数句柄或内联函数传递。
输出 y 是一个与输入 S 具有相同稀疏结构的稀疏矩阵。spfun 保持了稀疏结构,这与简单地将函数 f 应用于稀疏矩阵 S 不同——当 f (0) != 0 时,后者的结果会改变稀疏性。
示例
保持稀疏性的 spfun 与普通函数应用的对比
S = pi * speye (2,2) S = Compressed Column Sparse (rows = 2, cols = 2, nnz = 2 [50%]) (1, 1) -> 3.1416 (2, 2) -> 3.1416 y = spfun (@cos, S) y = Compressed Column Sparse (rows = 2, cols = 2, nnz = 2 [50%]) (1, 1) -> -1 (2, 2) -> -1
y = cos (S) y = Compressed Column Sparse (rows = 2, cols = 2, nnz = 4 [100%]) (1, 1) -> -1 (2, 1) -> 1 (1, 2) -> 1 (2, 2) -> -1
A = cellfun (@fcn, C) ¶A = cellfun ("fcn", C) ¶A = cellfun (fcn, C) ¶A = cellfun (fcn, C1, C2, …) ¶[A1, A2, …] = cellfun (…) ¶A = cellfun (…, "UniformOutput", val) ¶A = cellfun (…, "ErrorHandler", errfcn) ¶A = cellfun ('isempty', C) ¶A = cellfun ('islogical', C) ¶A = cellfun ('isnumeric', C) ¶A = cellfun ('isreal', C) ¶A = cellfun ('length', C) ¶A = cellfun ('numel', C) ¶A = cellfun ('prodofsize', C) ¶A = cellfun ('size', C, dim) ¶A = cellfun ('isclass', C, class) ¶对元胞数组 C 的元素执行函数 fcn。
cellfun 接受以字符串形式给定的任意函数 fcn(函数名称)、函数句柄或内联函数。建议优先使用字符串指定 fcn,因为对于内置函数,性能可提升约 3 倍;对于 m 文件,性能则相当。
cellfun 内置了有限数量的经过特殊编码的高性能函数(比函数句柄快约 8 倍)。这些函数仅当以字符串形式按名称指定时才会被使用,并且仅支持最简单的调用形式——cellfun ('fcn'), C)——且不带选项。如果你需要访问某个函数的重载版本(例如特定 classdef 文件的 numel),则不能使用加速函数名称,而必须改用函数句柄,例如 @numel。
高性能函数包括:
'isempty'对空元素返回 true。
'islogical'对逻辑元素返回 true。
'isnumeric'对数值元素返回 true。
'isreal'对实数元素返回 true。
'length'返回元胞元素长度的向量。
'ndims'返回每个元素的维度数。
'numel''prodofsize'返回每个元胞元素中包含的元素个数。该数值是每个元胞元素对象的各维度尺寸的乘积。
'size'返回沿维度 dim 的大小。
'isclass'对类型为 class 的元素返回 true。
C 中的元素被逐个传递给函数,每次函数调用的结果被收集到输出中。函数可以接受多个参数,输入参数由 C1、C2 等给出。单例(1x1)元胞输入参数将自动扩展为其他参数的大小。例如:
cellfun ("atan2", {1, 0}, {0, 1})
⇒ [ 1.57080 0.00000 ]
cellfun 的输出参数数量与函数的输出参数数量一致,且可以大于一个。当函数有多个输出时,它们将按如下方式收集到 cellfun 的输出参数中:
function [a, b] = twoouts (x)
a = x;
b = x*x;
endfunction
[aa, bb] = cellfun (@twoouts, {1, 2, 3})
⇒
aa =
1 2 3
bb =
1 4 9
注意,默认情况下,输出参数是与输入参数大小相同的数组。
如果参数 "UniformOutput" 设置为 true(默认值),则函数必须返回标量,这些标量将被拼接到返回数组中。如果 "UniformOutput" 为 false,则输出被拼接到元胞数组(或多个元胞数组)中。例如:
cellfun ("lower", {"Foo", "Bar", "FooBar"},
"UniformOutput", false)
⇒ {"foo", "bar", "foobar"}
参数 "ErrorHandler" 指定一个函数 errfcn,当 fcn 产生错误时调用。该函数的形式为:
function [...] = errfcn (s, ...)
其中,相对于 fcn,errfcn 有一个额外的输入参数 s。这是一个结构体,包含 "identifier"、"message" 和 "index" 三个字段,分别表示错误标识符、错误消息以及导致错误的输入参数的索引。例如:
function y = errfcn (s, x), y = NaN; endfunction
cellfun ("factorial", {-1, 2}, "ErrorHandler", @errfcn)
⇒ [NaN 2]
编程提示:请明智地使用 cellfun。cellfun 函数是避免循环的有用工具。它通常与匿名函数句柄一起使用;然而,调用匿名函数所涉及的开销与调用 m 文件函数的开销相当。传递内置函数的句柄更快,因为解释器不参与内部循环。例如:
C = {...}
v = cellfun (@(x) det (x), C); # 计算行列式
v = cellfun (@det, C); # 快 40%
A = structfun (fcn, S) ¶A = structfun (…, "ErrorHandler", errfcn) ¶A = structfun (…, "UniformOutput", val) ¶[A, B, …] = structfun (…) ¶对结构体 S 的每个字段执行名为 fcn 的函数。S 的字段被逐个传递给函数 fcn。
structfun 接受以内联函数、函数句柄或函数名称(以字符串形式)给出的任意函数 fcn。如果参数是字符串,则函数必须接受一个名为 x 的参数,并且必须返回一个字符串值。如果函数返回多个参数,它们将作为独立的输出变量返回。
如果参数 "UniformOutput" 设置为 true(默认值),则函数必须返回单个元素,该元素将被拼接到返回值中。如果 "UniformOutput" 为 false,则输出被放入一个与输入结构体具有相同字段名的结构体中。
s.name1 = "John Smith";
s.name2 = "Jill Jones";
structfun (@(x) regexp (x, '(\w+)$', "matches"){1}, s,
"UniformOutput", false)
⇒ scalar structure containing the fields:
name1 = Smith
name2 = Jones
给定参数 "ErrorHandler",errfcn 定义了当 fcn 产生错误时调用的函数。该函数的形式为:
function [...] = errfcn (se, ...)
其中,相对于 fcn,errfcn 有一个额外的输入参数 se。这是一个结构体,包含 "identifier"、"message" 和 "index" 三个字段,分别表示错误标识符、错误消息以及导致错误的输入参数的索引。有关如何使用错误处理程序的示例,请参阅 cellfun。
与之前的建议一致,尽可能使用 Octave 内置函数以获得最佳性能。此建议尤其适用于上述四个函数。例如,当按元素将两个数组相加时,可以使用内置加法函数的句柄 @plus,或者定义一个匿名函数 @(x,y) x + y。然而,匿名函数比第一种方法慢 60%。有关可能替代匿名函数使用的基本函数列表,请参阅 运算符重载。
版权所有 © 2024-2026 Octave中文网
ICP备案/许可证号:黑ICP备2024030411号-2