MATLAB中的Map函数是什么?

104

我有些惊讶MATLAB没有Map函数,因为这是我无法离开的功能之一。因此,我自己编写了一个,但是否有更好的版本?是否存在一个相对标准的MATLAB函数式编程库,我错过了吗?

function results = map(f,list)
% why doesn't MATLAB have a Map function?
results = zeros(1,length(list));
for k = 1:length(list)
    results(1,k) = f(list(k));
end

end

使用示例可以是:

map( @(x)x^2,1:10)

13
课程#1:从其他语言转换为Matlab:不要使用for循环,它们比向量化的解决方案慢几个数量级。 - CookieOfFortune
16
引入 JIT 后,for 循环不再像以前那样受到惩罚。 - MatlabDoug
@CookieOfFortune 我认为这已经不再是真的了... - Ander Biguri
2
@AnderBiguri 我认为他们已经添加了一些改进,但它仍然慢得多。 - CookieOfFortune
文件交换平台上的函数库包含mapfoldl(也称为reduce)、select(又称为filter)和其他不可或缺的好东西。如果你必须使用Matlab,强烈推荐使用。 - Ahmed Fasih
7个回答

138

简单来说,内置函数arrayfun与您的map函数在数字数组方面完全相同:

>> y = arrayfun(@(x) x^2, 1:10)
y =

     1     4     9    16    25    36    49    64    81   100

还有两个内置函数的行为类似:cellfun(操作cell数组元素)和structfun(对结构体的每个字段进行操作)。

然而,如果你利用向量化,特别是使用逐元素算术运算符,这些函数通常是不必要的。对于你给出的示例,向量化的解决方案如下:

>> x = 1:10;
>> y = x.^2
y =

     1     4     9    16    25    36    49    64    81   100

有些操作会自动在元素之间进行操作(如向量加上标量),而其他操作符则具有特殊的语法来进行逐元素操作(在运算符前加上.)。MATLAB中许多内置函数都是设计用于使用逐元素操作(通常应用于给定维度,例如summean等),因此不需要使用map函数。

总之,以下是一些对数组中的每个元素求平方的不同方法:

x = 1:10;       % Sample array
f = @(x) x.^2;  % Anonymous function that squares each element of its input

% Option #1:
y = x.^2;  % Use the element-wise power operator

% Option #2:
y = f(x);  % Pass a vector to f

% Option #3:
y = arrayfun(f, x);  % Pass each element to f separately
当然,对于这样一个简单的操作,选择方案1是最明智(也是最有效)的选择。

3
应该注意的是,选项1不仅更简单,而且更快(与选项3相比,选项2应该非常类似于选项1)! - Diederick C. Niehorster

10

除了向量和逐元素操作外,还有cellfun可用于将函数映射到单元格数组上。例如:

cellfun(@upper, {'a', 'b', 'c'}, 'UniformOutput',false)
ans = 
    'A'    'B'    'C'
如果 'UniformOutput' 为真(或未提供),它将尝试根据单元数组的维度将结果连接起来,因此:
cellfun(@upper, {'a', 'b', 'c'})
ans =
ABC

2

使用Matlab的向量化方法,有一个相当简单的解决方案:

a = [ 10 20 30 40 50 ]; % the array with the original values
b = [ 10 8 6 4 2 ]; % the mapping array
c = zeros( 1, 10 ); % your target array

现在,输入:
c( b ) = a

返回

c = 0    50     0    40     0    30     0    20     0    10

c(b)是一个大小为5的向量引用,其中b给出的索引处的元素是c的元素。如果您给该引用向量赋值,则原来在c中的值将被覆盖,因为c(b)包含对c中值的引用而不是副本。


1

看起来内置的arrayfun在需要返回函数数组的情况下无法正常工作: 例如: map(@(x)[x x^2 x^3],1:10)

以下轻微修改可以使其更好地工作:

function results = map(f,list)
% why doesn't MATLAB have a Map function?
for k = 1:length(list)
    if (k==1)
        r1=f(list(k));
        results = zeros(length(r1),length(list));
        results(:,k)=r1;
    else
        results(:,k) = f(list(k));

    end;
end;
end

6
对于你的例子,ARRAYFUN 可以起作用,你只需包括输入参数..., 'UniformOutput', false)以创建包含数组的单元格数组输出,然后按照需要将它们格式化和组合成非单元素数组。 - gnovice

0

如果Matlab没有内置的地图函数,那可能是因为要考虑效率问题。在你的实现中,你正在使用循环来迭代列表的元素,这在Matlab世界中通常是不被看好的。大多数内置的Matlab函数都是“向量化”的,即在整个数组上调用函数比自己迭代数组并为每个元素调用函数更有效率。

换句话说,这个:


a = 1:10;
a.^2

比这快得多


a = 1:10;
map(@(x)x^2, a)

假设您对映射的定义。


2
我认为他的观点并不是他必须要循环,而只是要指定其结果为将提供的函数应用于提供的数组的相应元素的结果数组。我不太了解Matlab,但似乎arrayfun可以胜任这项工作。 - user370536
1
大多数内置的Matlab函数和运算符已经能做到这一点:它们作用于输入数组的每个元素,并返回相应的结果数组。 - Dima

0

你不需要使用map,因为应用于值列表的标量函数会应用于每个值,因此类似于map。只需尝试:

l = 1:10
f = @(x) x + 1

f(l)

在你的特定情况下,甚至可以编写如下内容。
l.^2

9
实际上,这是不正确的。Matlab没有足够强大的类型系统来指定标量函数。在你的例子中,使用向量调用f并执行单个向量加法。为了验证这一点,请对你的代码样本进行剖析(在运行代码之前键入“profile on”,然后在运行后输入“profile off report”)。你会看到只有一个对f的调用。 - Mr Fooz

-1

根据之前的答案所描述的,将解决方案向量化可能是速度最快的最佳解决方案。向量化也非常适合Matlab,并且感觉很好。

话虽如此,Matlab现在确实有一个Map容器类。

请参见http://www.mathworks.com/help/matlab/map-containers.html


Op 正在谈论高阶函数,即 cellfun 等,而不是哈希表或键值对。 - Ahmed Fasih

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接