Fonction de carte dans MATLAB?

100

Je suis un peu surpris que MATLAB n'ait pas de fonction Map, alors j'en ai piraté une moi-même car c'est quelque chose dont je ne peux pas me passer. Existe-t-il une meilleure version? Existe-t-il une bibliothèque de programmation fonctionnelle quelque peu standard pour MATLAB qui me manque?

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

l'utilisation serait par exemple

map( @(x)x^2,1:10)
Will Ness
la source
12
Leçon n ° 1 pour passer d'autres langages à Matlab: N'utilisez pas les boucles for, elles sont quelques ordres de grandeur plus lentes qu'une solution vectorisée.
CookieOfFortune
15
Avec l'introduction du JIT, les boucles for ne subissent pas la pénalité qu'elles avaient autrefois.
MatlabDoug
@CookieOfFortune Je pense que ce n'est plus vrai ...
Ander Biguri
2
@AnderBiguri Je pense qu'ils ont ajouté quelques améliorations mais c'est encore beaucoup plus lent.
CookieOfFortune
La bibliothèque fonctionnelle sur l'échange de fichiers a map, foldl(également connu sous le nom de reduce), select(aka filter), et d'autres goodies indispensables. Recommandé (si vous devez utiliser Matlab).
Ahmed Fasih

Réponses:

133

La réponse courte: la fonction intégrée arrayfunfait exactement ce que votre mapfonction fait pour les tableaux numériques:

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

     1     4     9    16    25    36    49    64    81   100

Il existe deux autres fonctions intégrées qui se comportent de la même manière: cellfun(qui opère sur des éléments de tableaux de cellules) et structfun(qui opère sur chaque champ d'une structure).

Cependant, ces fonctions ne sont souvent pas nécessaires si vous tirez parti de la vectorisation, en particulier en utilisant des opérateurs arithmétiques élémentaires . Pour l'exemple que vous avez donné, une solution vectorisée serait:

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

     1     4     9    16    25    36    49    64    81   100

Certaines opérations opéreront automatiquement sur les éléments (comme l'ajout d'une valeur scalaire à un vecteur) tandis que d'autres opérateurs ont une syntaxe spéciale pour l'opération élément par élément (indiquée par un .avant l'opérateur). De nombreuses fonctions intégrées dans MATLAB sont conçues pour fonctionner sur des arguments vectoriels et matriciels en utilisant des opérations élémentaires (souvent appliquées à une dimension donnée, comme sumet meanpar exemple), et ne nécessitent donc pas de fonctions de carte.

Pour résumer, voici quelques façons différentes de mettre au carré chaque élément d'un tableau:

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

Bien sûr, pour une opération aussi simple, l'option n ° 1 est le choix le plus judicieux (et efficace).

novice
la source
2
Il faut noter que l'option 1 est non seulement plus simple, mais aussi plus rapide (par rapport à l'option 3, 2 devrait être très similaire à 1)!
Diederick C.Niehorster
10

En plus des opérations vectorielles et élémentaires, il existe également cellfundes fonctions de mappage sur des tableaux de cellules. Par exemple:

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

Si 'UniformOutput' est vrai (ou non fourni), il tentera de concaténer les résultats en fonction des dimensions du tableau de cellules, donc

cellfun(@upper, {'a', 'b', 'c'})
ans =
ABC
Kwatford
la source
2

Une solution assez simple, utilisant la vectorisation de Matlab serait:

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

Maintenant, en tapant

c( b ) = a

Retour

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

c (b) est une référence à un vecteur de taille 5 avec les éléments de c aux indices donnés par b. Maintenant, si vous assignez des valeurs à ce vecteur de référence, les valeurs d'origine de c sont écrasées, car c (b) contient des références aux valeurs de c et aucune copie.

doc
la source
1

Il semble que le arrayfun intégré ne fonctionne pas si le résultat nécessaire est un tableau de fonctions: par exemple: map (@ (x) [xx ^ 2 x ^ 3], 1: 10)

de légers mods ci-dessous améliorent ce fonctionnement:

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
Foo Bara
la source
5
ARRAYFUN fonctionnerait pour votre exemple, il vous suffirait d'inclure les arguments d'entrée ..., 'UniformOutput', false);pour créer une sortie de tableau de cellules contenant vos tableaux, puis de les formater et de les combiner comme vous le souhaitez dans un tableau non cellulaire.
gnovice
0

Si matlab n'a pas de fonction de carte intégrée, cela peut être dû à des considérations d'efficacité. Dans votre implémentation, vous utilisez une boucle pour parcourir les éléments de la liste, ce qui est généralement mal vu dans le monde matlab. La plupart des fonctions matlab intégrées sont "vectorisées", c'est-à-dire qu'il est plus efficace d'appeler une fonction sur un tableau entier que de l'itérer soi-même et d'appeler la fonction pour chaque élément.

En d'autres termes, ce


a = 1:10;
a.^2

est beaucoup plus rapide que ça


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

en supposant votre définition de la carte.

Dima
la source
2
Je pense que son argument n'était pas qu'il voulait nécessairement qu'il soit en boucle, mais simplement qu'il soit spécifié comme ayant comme résultat le tableau des résultats de l'application de la fonction fournie aux éléments correspondants du tableau fourni. Je ne connais pas grand chose à matlab, mais il semble que arrayfun fasse le travail.
1
La plupart des fonctions et opérateurs Matlab intégrés le font déjà: ils opèrent sur chaque élément du tableau d'entrée et renvoient un tableau de résultats correspondant.
Dima
0

Vous n'en avez pas besoin mapcar une fonction scalaire appliquée à une liste de valeurs est appliquée à chacune des valeurs et fonctionne donc de manière similaire à map. Essayez juste

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

f(l)

Dans votre cas particulier, vous pourriez même écrire

l.^2
Dario
la source
9
-1: Ce n'est en fait pas vrai. Matlab n'a pas de système de types suffisamment fort pour spécifier des fonctions scalaires. f est appelée avec le vecteur et une seule addition de vecteur est effectuée dans votre exemple. Pour vérifier cela, profilez votre exemple de code ("profile on" avant d'exécuter le code, puis "profile off report" après). Vous verrez qu'il n'y a qu'un seul appel à f.
Mr Fooz le
-1

Vectoriser la solution comme décrit dans les réponses précédentes est probablement la meilleure solution pour la vitesse. La vectorisation est également très matlaby et se sent bien.

Cela dit, Matlab a maintenant une classe de conteneur Map.

Voir http://www.mathworks.com/help/matlab/map-containers.html

TallBrian
la source
Op parle de la fonction d'ordre supérieur, c'est-à-dire, cellfunet al., Pas de tables de hachage ou de paires clé-valeur.
Ahmed Fasih