Функция карты в MATLAB?

100

Я немного удивлен, что в 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)
Уилл Несс
источник
12
Урок №1, переходящий с других языков на Matlab: не используйте циклы for, они на несколько порядков медленнее, чем векторизованное решение.
CookieOfFortune,
15
С введением JIT циклы for не имеют штрафов, которые они когда-то применяли.
MatlabDoug
@CookieOfFortune Я думаю, что это уже не так ...
Андер Бигури
2
@AnderBiguri Я думаю, они добавили некоторые улучшения, но все еще намного медленнее.
CookieOfFortune
Функциональная библиотека на файловой бирже map, foldl(также известный как reduce), select(он же filter), и другие незаменимые лакомства. Рекомендуется (если вам нужно использовать Matlab).
Ахмед Фасих

Ответы:

133

Краткий ответ: встроенная функция arrayfunделает то же самое, что и ваша mapфункция для числовых массивов:

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

     1     4     9    16    25    36    49    64    81   100

Есть две другие встроенные функции, которые ведут себя аналогично: cellfun(которая работает с элементами массивов ячеек) и structfun(которая работает с каждым полем структуры).

Однако в этих функциях часто нет необходимости, если вы пользуетесь преимуществами векторизации, в частности, с помощью поэлементных арифметических операторов . В приведенном вами примере векторизованное решение будет:

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

     1     4     9    16    25    36    49    64    81   100

Некоторые операции будут автоматически выполняться с элементами (например, добавление скалярного значения к вектору), в то время как другие операторы имеют специальный синтаксис для поэлементных операций (обозначается символом .перед оператором). Многие встроенные функции в MATLAB предназначены для работы с векторными и матричными аргументами с использованием поэлементных операций (часто применяемых к заданному измерению, например, sumи mean), и, таким образом, не требуют функций карты.

Подводя итог, вот несколько различных способов возвести каждый элемент массива в квадрат:

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 - наиболее разумный (и эффективный) выбор.

Гновице
источник
2
Следует отметить, что вариант 1 не только проще, но и быстрее (по сравнению с вариантом 3, 2 должен быть очень похож на 1)!
Diederick C. Niehorster 05
10

Помимо векторных и поэлементных операций, есть также cellfunфункции отображения на массивы ячеек. Например:

cellfun(@upper, {'a', 'b', 'c'}, 'UniformOutput',false)
ans = 
    'A'    'B'    'C'

Если UniformOutput имеет значение true (или не предоставлено), он попытается объединить результаты в соответствии с размерами массива ячеек, поэтому

cellfun(@upper, {'a', 'b', 'c'})
ans =
ABC
Kwatford
источник
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 с элементами c в индексах, заданных b. Теперь, если вы Ассинг значения этого опорного вектора, исходные значения в с переписываются, так как с (б) содержит ссылки на значения в с и без каких-либо копий.

док
источник
1

Кажется, что встроенный arrayfun не работает, если требуемый результат представляет собой массив функций: например: map (@ (x) [xx ^ 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
Фу Бара
источник
5
ARRAYFUN будет работать для вашего примера, вам просто нужно будет включить входные аргументы ..., 'UniformOutput', false);для создания вывода массива ячеек, содержащего ваши массивы, затем отформатировать и объединить их, как вы хотите, в массив без ячеек.
gnovice
0

Если в Matlab нет встроенной функции карты, это может быть связано с соображениями эффективности. В вашей реализации вы используете цикл для перебора элементов списка, что обычно не одобряется в мире Matlab. Большинство встроенных функций Matlab являются «векторизованными», то есть более эффективно вызывать функцию для всего массива, чем выполнять итерацию по нему самостоятельно и вызывать функцию для каждого элемента.

Другими словами, это


a = 1:10;
a.^2

намного быстрее, чем это


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

предполагая ваше определение карты.

Дима
источник
2
Я думаю, его точка зрения заключалась не в том, что он хотел, чтобы он обязательно выполнялся в цикле, а в том, чтобы просто указать как имеющий в качестве результата массив результатов применения предоставленной функции к соответствующим элементам предоставленного массива. Я не очень хорошо разбираюсь в Matlab, но кажется, что arrayfun выполняет свою работу.
1
Большинство встроенных функций и операторов Matlab уже делают это: они работают с каждым элементом входного массива и возвращают соответствующий массив результатов.
Дима
0

В этом нет необходимости, mapпоскольку скалярная функция, которая применяется к списку значений, применяется к каждому из значений и, следовательно, работает аналогично map. Просто попробуй

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

f(l)

В вашем конкретном случае вы даже можете написать

l.^2
Дарио
источник
9
-1: На самом деле это неправда. Matlab не имеет достаточно сильной системы типов, чтобы определять скалярные функции. f вызывается с вектором, и в вашем примере выполняется сложение одного вектора. Чтобы убедиться в этом, профилируйте образец кода («профиль включен» перед запуском кода, затем «профиль отключен» после него). Вы увидите, что есть единственный вызов f.
Mr Fooz,
-1

Векторизация решения, как описано в предыдущих ответах, вероятно, лучшее решение для скорости. Векторизация тоже очень Matlaby и приятная.

С учетом сказанного у Matlab теперь есть класс контейнера Map.

См. Http://www.mathworks.com/help/matlab/map-containers.html

Высокий Брайан
источник
Op говорит о функциях высшего порядка, т. cellfunЕ. И др., А не о хеш-таблицах или парах ключ-значение.
Ахмед Фасих