19.3 函数应用

一般来说,函数在设计时就应该考虑矩阵参数,并以向量化的方式处理整个矩阵运算。然而,由于各种原因,有时以这种方式编写函数显得困难甚至不可能。针对这些情况,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 可以是逻辑数组、数值数组、字符串数组、结构体数组或元胞数组。arrayfunA 的所有元素逐个传递给函数 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, ...)

其中,相对于 fcnerrfcn 有一个额外的输入参数 s。这是一个结构体,包含 "identifier""message""index" 三个字段,分别表示错误标识符、错误消息以及导致错误的数组元素的索引。errfcn 的输出参数大小必须与 fcn 的输出参数大小相同,否则将引发真正的错误。例如:

function y = ferr (s, x), y = "MyString"; endfunction
arrayfun (@str2num, [1234],
          "UniformOutput", false, "ErrorHandler", @ferr)
⇒ 
   {
     [1,1] = MyString
   }

另请参阅: spfuncellfunstructfun.

 
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

另请参阅: arrayfuncellfunstructfun.

 
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 中的元素被逐个传递给函数,每次函数调用的结果被收集到输出中。函数可以接受多个参数,输入参数由 C1C2 等给出。单例(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, ...)

其中,相对于 fcnerrfcn 有一个额外的输入参数 s。这是一个结构体,包含 "identifier""message""index" 三个字段,分别表示错误标识符、错误消息以及导致错误的输入参数的索引。例如:

function y = errfcn (s, x), y = NaN; endfunction
cellfun ("factorial", {-1, 2}, "ErrorHandler", @errfcn)
⇒  [NaN 2]

编程提示:请明智地使用 cellfuncellfun 函数是避免循环的有用工具。它通常与匿名函数句柄一起使用;然而,调用匿名函数所涉及的开销与调用 m 文件函数的开销相当。传递内置函数的句柄更快,因为解释器不参与内部循环。例如:

C = {...}
v = cellfun (@(x) det (x), C); # 计算行列式
v = cellfun (@det, C);         # 快 40%

另请参阅: arrayfunstructfunspfun.

 
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, ...)

其中,相对于 fcnerrfcn 有一个额外的输入参数 se。这是一个结构体,包含 "identifier""message""index" 三个字段,分别表示错误标识符、错误消息以及导致错误的输入参数的索引。有关如何使用错误处理程序的示例,请参阅 cellfun

另请参阅: cellfunarrayfunspfun.

与之前的建议一致,尽可能使用 Octave 内置函数以获得最佳性能。此建议尤其适用于上述四个函数。例如,当按元素将两个数组相加时,可以使用内置加法函数的句柄 @plus,或者定义一个匿名函数 @(x,y) x + y。然而,匿名函数比第一种方法慢 60%。有关可能替代匿名函数使用的基本函数列表,请参阅 运算符重载


版权所有 © 2024-2026 Octave中文网

ICP备案/许可证号:黑ICP备2024030411号-2