Search code examples
matlabprogresscommand-window

Printing progress in command window


I'd like to use fprintf to show code execution progress in the command window.

I've got a N x 1 array of structures, let's call it myStructure. Each element has the fields name and data. I'd like to print the name side by side with the number of data points, like such:

name1    number1
name2    number2
name3    number3
name4    number4
...

I can use repmat N times along with fprintf. The problem with that is that all the numbers have to come in between the names in a cell array C.

fprintf(repmat('%s\t%d',N,1),C{:})

I can use cellfun to get the names and number of datapoints.

names = {myStucture.name};
numpoints = cellfun(@numel,{myStructure.data});

However I'm not sure how to get this into a cell array with alternating elements for C to make the fprintf work.

Is there a way to do this? Is there a better way to get fprintf to behave as I desire?


Solution

  • You're very close. What I would do is change your cellfun call so that the output is a cell array instead of a numeric array. Use the 'UniformOutput' flag and set this to 0 or false.

    When you're done, make a new cell array where both the name cell array and the size cell array are stacked on top of each other. You can then call fprintf once.

    % Save the names in a cell array
    A = {myStructure.name};
    
    % Save the sizes in another cell array
    B = cellfun(@numel, {myStructure.data}, 'UniformOutput', 0);
    
    % Create a master cell array where the first row are the names
    % and the second row are the sizes
    out = [A; B];
    
    % Print out the elements side-by-side
    fprintf('%s\t%d\n', out{:});
    

    The trick with the third line of code is that when you unroll the cell array using {:}, this creates a comma-separated list unrolled in column-major format, and so doing out{:} actually gives you:

    A{1}, B{1}, A{2}, B{2}, ..., A{n}, B{n}
    

    ... which provides the interleaving you need. Therefore, providing this order into fprintf coincides with the format specifiers that are specified and thus gives you what you need. That's why it's important to stack the cell arrays so that each column gives the information you need.


    Minor Note

    Of course one should never forget that one of the easiest ways to tackle your problem is to just use a simple for loop. Even though for loops are considered bad practice, their performance has come a long way throughout MATLAB's evolution.

    Simply put, just do this:

    for ii = 1 : numel(myStructure)
        fprintf('%s\t%d\n', myStructure(ii).name, numel(myStructure(ii).data));
    end
    

    The above code is arguably more readable in comparison to what we did above with cell arrays. You're accessing the structure directly rather than having to create intermediate variables for the purpose of calling fprintf once.


    Example Run

    Here's an example of this running. Using the data shown below:

    clear myStructure;
    myStructure(1).name = 'hello';
    myStructure(1).data = rand(5,1);
    myStructure(2).name = 'hi';
    myStructure(2).data = zeros(3,3);
    myStructure(3).name = 'huh';
    myStructure(3).data = ones(6,4);
    

    I get the following output after running the printing code:

    hello   5
    hi  9
    huh 24
    

    We can see that the sizes are correct as the first element in the structure is simply a random 5 element vector, the second element is a 3 x 3 = 9 zeroes matrix while the last element is a 6 x 4 = 24 ones matrix.