cellfun for objects

Hi, I would like to run the following line of code
lhs=(cellfun(@eval,script));
script is a cell array and the content of each cell is a function of elements of a class called myclass. I get the following error message:
myclass output type is not currently implemented
Is there any workaround? Thanks, J.

 Respuesta aceptada

Daniel Shub
Daniel Shub el 26 de Ag. de 2011

1 voto

I suggest replacing
script = {'a+b'; 'c-d'}
with
script = {@(a,b,c,d)(a+b); @(a,b,c,d)(c-d)};
You can do the conversion by doing something awful like:
script = cellfun(@eval, cellfun(@(x)(['@(a,b,c,d)(', x, ')']), script, 'UniformOutput', false), 'UniformOutput', false);
Note this does not require a, b, c, or d to be defined at the time of defining script. Rather, it just requires you to know the maximum number of objects in advance (here I stopped at 4, but you can use as many as you like).
With this version of script you can then define your objects
a = 1; b = 2; c = 3; d = 4;
and run script without eval:
y = cellfun(@(x)(x(a,b,c,d)), script);
Depending on your actual functions, you might need to set the UniformOutput flag to false.

Más respuestas (9)

Sean de Wolski
Sean de Wolski el 25 de Ag. de 2011

1 voto

I don't know if there is a way to make that work, but I can advise against doing it!
Don't Do It!

6 comentarios

Jan
Jan el 25 de Ag. de 2011
+1: It is obvious, that the strange EVAL method causes problems. The additional abstraction layer of calling the class indirectly increases the confusion level.
Junior
Junior el 25 de Ag. de 2011
Hi Sean, Thanks for making me aware of the warnings against eval. In this case, however, I have full control of what eval does and have thoroughly tested it. Though if I had an alternative, I would use the alternative.
Sean de Wolski
Sean de Wolski el 25 de Ag. de 2011
What about writing everything in the cell to an mfile with fprintf, and then running the mfile?
Junior
Junior el 25 de Ag. de 2011
Your intuition is correct. Writing an m-file function would do the trick. But I am trying to move away from creating/and saving the m-file in the first place.
Sean de Wolski
Sean de Wolski el 25 de Ag. de 2011
I'll bet you'll regret that later (3 months, 6 months, 2 years from now) when you want a record of what you did... Just a few thoughts from experience. Save the mfile with the date in the title if you have to, that'll make them easy to sort and and easy find.
Junior
Junior el 25 de Ag. de 2011
The reason I don't do that is because the potential content of the m-file is part of some other object.

Iniciar sesión para comentar.

Junior
Junior el 25 de Ag. de 2011

1 voto

Summarizing the steps:
lhs=(cellfun(@eval,script,'UniformOutput',false));
lhs=[lhs{:}];
lhs=reshape(lhs,size(script));
Jan
Jan el 25 de Ag. de 2011

0 votos

I have no idea, what you are doing. But when I read the message "myclass output type is not currently implemented", I do not think, that there is any workaround but implementing the output type.

2 comentarios

Junior
Junior el 25 de Ag. de 2011
I have no idea what cellfun does:-) it is a builtin function. I can successfully evaluate the content of each cell in script. myclass returns and object, which is like a structure.
Fangjun Jiang
Fangjun Jiang el 25 de Ag. de 2011
cellfun() does the function specified on every element of the cell array.

Iniciar sesión para comentar.

Fangjun Jiang
Fangjun Jiang el 25 de Ag. de 2011

0 votos

I am not sure about your class. But there is really no problem to make it work if you want it. You just need to make sure all the command scripts in the cell contents are valid (including arguments if any). Can you use a MATLAB class/object to provide an example to explain what you want?
script={'magic(4)','rand(4)'};
a=cellfun(@eval,script,'uni',0)

10 comentarios

Sean de Wolski
Sean de Wolski el 25 de Ag. de 2011
Not with me! (Well I tried to fool it)
*SAVE EVERYTHING BEFORE YOU RUN THIS, IT CAUSES CLOSURE*
sc = {'clear';'A=magic(4)';'B=A*rand(4,1)'}
cellfun(@eval,sc)
Junior
Junior el 25 de Ag. de 2011
I have not tried your example, but both magic(4) and rand(4) are elements of a class known to matlab...
Anyway, here is something that would work but it not nice-looking as it requires (unnecessary lines of codes). First
lhs=(cellfun(@eval,script,'UniformOutput',false));
goes through but returns a cell array (instead of the matrix or vector that I want). I tried lhs=cell2mat(lhs); but that does not work. One thing that works is
lhs=[lhs{:}];
The problem is that after all this, I still have to reshape lhs into the format of script.
lhs=reshape(lhs,size(script));
Sean de Wolski
Sean de Wolski el 25 de Ag. de 2011
I think you're digging yourself into a mighty deep hole with that one. Why don't you describe the overall goal to us, maybe we can point you somewhere else.
Fangjun Jiang
Fangjun Jiang el 25 de Ag. de 2011
Yes, because the command in your script could return any size of data, you have to use the (...'uni',false) option and the results are stored in a cell array. You have to do whatever necessary to get the format you want.
Fangjun Jiang
Fangjun Jiang el 25 de Ag. de 2011
@Sean. Yes, your example crashed my MATLAB too!
@Junior, please explain your goal and there might be better option.
Junior
Junior el 25 de Ag. de 2011
Fangjun, Here is a summary of what I do. I read a text file, out of which I create different objects, with one of the objects having the others as attributes. The only thing that is stored on disk is the original text file. Although small objects contain char elements, they are concatenated into cell arrays. This makes it convenient to use cellfun on them. The alternative is to read the original text file and create m-files to call. But then, this would imply that I create several m-files for each text file I read.
Sean de Wolski
Sean de Wolski el 25 de Ag. de 2011
That's not so bad. For each textfile create:
-a directory
-a driver script to drive the mfiles
-the various mfiles.
Fangjun Jiang
Fangjun Jiang el 25 de Ag. de 2011
@Junior, Okay. It sounds like you've worked it through using cellfun() and your class. My understand is that you need to read original data from text file and assign them to the attributes of your object. If class/object is not required, it might be a good idea to use structure. See help struct
Junior
Junior el 25 de Ag. de 2011
@Fangjun, unfortunately structures are not enough here. I really need a class here.
@Sean, what you are suggesting is the option I had before and it may or may not be convenient. If I have say 100 files to read, I will then need as many directories, each one including many files.
Sean de Wolski
Sean de Wolski el 25 de Ag. de 2011
At a few kilobytes per file it may make it to a megabyte at some point :)

Iniciar sesión para comentar.

Jan
Jan el 25 de Ag. de 2011

0 votos

CELLFUN is doing this (uniform output assumed):
function lhs = CellFun_M(Fcn, C)
for iC = numel(C):-1:1 % Backward for fast allocation
lhs(i) = feval(Fcn, C{i});
end
EVAL needs a string for the evaluation. But "the content of each cell is a function of elements of a class called myclass" sounds, like your cell (with the strange name "script") does not contain strings.
Unfortunately modern MATLAB releases do not contain the source code "cellfun.c" anymore. What a pitty. It was a nice piece of code for education.

1 comentario

Junior
Junior el 25 de Ag. de 2011
Jan, what you suggest here would do with just eval as the content of each cell is a string. But the output of the evaluation process is an object of type "myclass". Gosh! I always forget that thing of running the loops backward. Thanks for reminding me.

Iniciar sesión para comentar.

Daniel Shub
Daniel Shub el 26 de Ag. de 2011

0 votos

lhs=(cellfun(@eval,script));
I got to say that this looks like the absolute worse use of eval that I have ever seen. You say that "script is a cell array and the content of each cell is a function of elements of a class called myclass". I interpret this as meaning:
script = {'obj1([2,4,6]).methodA'; 'obj2([3,4,5]).methodB'};
If this is the case, you could probably parse the strings in script, and run them directly without eval. Or even better would be to run them before creating the string.

7 comentarios

Junior
Junior el 26 de Ag. de 2011
Thanks for the compliment about the "worse use of eval". In each cell there is a string with some operations. For instance,
script ={'x+y'; 'a-b'}
This requires eval, right? Now, after running eval, the operations are carried in the space/class of the variables existing in the workspace. In the example above, x,y,a,b would objects of class myclass. Does this make sense?
Junior
Junior el 26 de Ag. de 2011
About parsing, it is precisely the results of a parsing operation that creates the strings that I put in a cell, with the view of creating a matrix whose elements are myclass objects.
Sean de Wolski
Sean de Wolski el 26 de Ag. de 2011
I agree with Daniel. One of the reasons we recommend against using eval is that it easily and reliably causes bugs that are very difficult to locate and debug. You already seem to be having that problem. As long as you're reluctant to look into other more standard solutions, you'll continue to have the problem and likely be unable to solve it.
Also, why bother using cellfun? Might as well just use a for-loop, they're just as fast (faster on my system actually) and give you more options.
Daniel Shub
Daniel Shub el 26 de Ag. de 2011
What about replacing script with a bunch of anonymous function handles that have the footprint @(a,b,...,z)().
script={@(a,b,...,z)(x+y), @(a,b,...,z)(a-b)}
I think this would avoid the eval and might be less error prone.
Junior
Junior el 26 de Ag. de 2011
@ Daniel: What you are suggesting here is interesting. I hope you can tell me more about how to execute it. In any case, I don't see how it would not work for me as it requires that all the variables be specified in @(). Before calling cellfun on the script, I first create some variables in the workspace.
@Sean: I don't see a way to avoid using eval. I really would like to if it is possible. Even if I ran for loops, I would still have to use eval as the expressions to evaluate are strings. By the way, could anyone of you tell me how to write an anonymous function using strings?
Sean de Wolski
Sean de Wolski el 26 de Ag. de 2011
I've already recommended a way twice, and you agreed it would work! If you'd spent a fraction of the time that you've spent trying to debug something that might not even be possible you could probably have a fully automated application that given a batch of text files creates n directories with code to run each file and the results, and a driver script. You would then have a record of everything you've done, be able to use the full (very powerful) suite of MATLAB debugging tools, and have something that you or the next person to follow behind you stands a fighting chance of being able to figure out what you did in the future (this is a run-on sentence), there all of the advantages, and with the exception of having to take a few minutes at a chalk (or white) board to figure out an organized directory structure, none of the disadvantages.
Junior
Junior el 26 de Ag. de 2011
Dear Sean, I fully agree with that and I say it again I know how to do it that way. But I have also said that that is where I come from and I am trying to move away from that. This has consequences as you pointed out, but not necessarily the ones you are mentioning. If successful, the way I am doing things will be easiear for the users to understand what is going on and debug whatever is necessary.

Iniciar sesión para comentar.

Junior
Junior el 26 de Ag. de 2011

0 votos

Hi Daniel, maybe you are onto something here. If I follow that route, I will have something like
script={'@(a,b,c,d)(a+b)'; '@(a,b,c,d)(c-d)'};
The content of each cell will have to be a string. The content of each cell is the result of parsing a file. Defining a string str0='@(a,b,c,d)';, each element of the cell array will have to be built as
script{i,j}=strcat(str0,string_ij);
where in this example, string_11='a+b';
Another problem is that the are some constants that need to be created before evaluating the script. Here is an example:
c1=1; c2=f(c1); c3=h(c2,c3); etc.
In the current implementation, typical entries of script look as follows
script={'x(1)+c1*x(2)+log(x(3))','x(4)-c3*exp(x(2)-x(3))'}
while c1, c2,...,cn are numbers, x on the other hand is a vector of elements of class myclass.
Junior
Junior el 26 de Ag. de 2011

0 votos

I thought it might be instructive to see what the m-file would look like, the implementation i had earlier and that Sean is recommending.
function out=myfunc(x,a)
c1=f1(a);
c2=f2(a,c1);
c3=f3(a,c1,c2);
out(1,2)=myclass; % initialize output
out(1)=x(1)+c1*x(2)+log(x(3));
out(2)=x(4)-c3*exp(x(2)-x(3));
This is the typical function I would like to avoid writing to disk
Junior
Junior el 28 de Ag. de 2011

0 votos

This is what I finally did, based on Daniel's advice.
script = {str2func('@(a,b,c,d)(a+b)'); str2func('@(a,b,c,d)(c-d)')};
lhs=cellfun(@(x)(x(a,b,c,d)),script,'UniformOutput',false);
lhs=[lhs{:}];
lhs=reshape(lhs,size(script));
with this, I got rid of eval altogether and it works fine.

4 comentarios

Daniel Shub
Daniel Shub el 28 de Ag. de 2011
and yet you decided not to accept my answer or even given me a vote :(
Junior
Junior el 28 de Ag. de 2011
Dear Daniel, I gave you full credits and I have now also voted for you. Tell me if there is anything more I can do. With respect to accepting an answer, you also should tell me what to do here. Shall I accept your last answer that does not include the small changes I made? Your call!
Walter Roberson
Walter Roberson el 29 de Ag. de 2011
You should accept the answer that got you the closest to what you needed to do. Very few people are going to be able to get you *exactly* to where you need to get as you live with program structures and variable names and time limitations and skill limitations and so on that you did not document here.
Daniel Shub
Daniel Shub el 30 de Ag. de 2011
I don't really care if you accept my answer, your answer, or someone else's answer. If the problem is solved, then please accept an answer.

Iniciar sesión para comentar.

Categorías

Más información sobre Variables en Centro de ayuda y File Exchange.

Preguntada:

el 25 de Ag. de 2011

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by