Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

Happy Halloween!

Today I’d like to introduce my guest bloggers Sarah Wait Zaranek and Jiro Doke. Sarah previously has written about speeding up code from a customer to get acceptable performance and Jiro has previously written about making presentation quality graphics and is a co-author of File Exchange Pick of the Week.

Contents

Halloween

In some parts of the world, people celebrate Halloween, which occurs on October 31. In the spirit (pun intended) of the holiday, we created a Halloween-inspired function.

If you have ever used why in MATLAB, you will definitely recognize the other inspiration for our function.

Let's see what it does

I wonder what Loren is going to be for Halloween!

halloween Loren
Loren, you are scary enough without a costume!

How about not just any Loren, but our Loren?

halloween Loren Shure
Loren Shure, for Halloween, you will be a screaming Gremlin.

Oh my! What about Sarah and Jiro?

halloween Sarah
halloween Jiro
Sarah, for Halloween, you will be a sleepy Bogeyman swimming in blood.
Jiro, for Halloween, you will be a Dracula baring teeth to the ends of the earth.

Note that the function works with both Command and Function syntax, which is explained here.

What happens if we don't put a name in?

halloween
Nameless, you are scary enough without a costume!

It still works. Looks like Nameless is going to have a fun holiday.

Dissecting the function

The main function does several things

  • loads Halloween data from a MAT-file
  • generates a Halloween name based on the name entered and the current time
  • prints the Halloween name to the screen

It also includes an "easter egg".

Let's look inside halloween.

type halloween
function halloween(varargin)
% HALLOWEEN   Print out your Halloween name.
%     HALLOWEEN NAME prints a randomly generated Halloween name based on
%     NAME (optional). NAME must be a character array or multiple character
%     arrays.
%
% Example:
%     halloween('Loren')
%     halloween('Sarah Zaranek')
%     halloween Jiro Doke

% Copyright 2011 The MathWorks, Inc.

% Default name
if nargin == 0
    name = 'Nameless';
else
    % Make sure all inputs are character arrays
    cellfun(@(x,y) validateattributes(x, {'char'}, {'row'}, y), ...
        varargin, num2cell(1:nargin));
    % Construct a single name from multiple strings
    name = deblank(sprintf('%s ', varargin{:}));
end

% Special case
[~, ~, ~, ~, ~, sec] = datevec(now);
rng(sec*1000);

if randi(100) > 95  % 5 percent chance of getting this special message.
    fprintf('%s, you are scary enough without a costume!', name);
    return;
end

% Set seed based on name and time.
seed = sum(name) + floor(now);
rng(seed);

% Load Halloween data
data = load('HalloweenData');

% Generate Halloween name
halloween_name = getName(data);

% Print out Halloween name
printName(name, halloween_name);

We will dig into getName and printName shortly, but let's just see what halloween does.

Default Name

First, we check to see if the user supplied a name. This uses nargin which is highly useful when writing your own custom functions. nargin gives the number of arguments the user inputs when calling the function. If it is zero, we assign a default name of "Nameless". This is an easy way to set default behavior for your function - allowing users to either run it with or without input arguments. Take a look at this previous post by Loren on this topic.

If an input (or inputs) is provided, we do a check to see if they are valid by using validateattributes. Also, to provide a nice way of calling using Command syntax, we allow multiple input arguments to construct a longer string (see the example above with "Loren Shure").

   if nargin == 0
       name = 'Nameless';
   else
       % Make sure all inputs are character arrays
       cellfun(@(x,y) validateattributes(x, {'char'}, {'row'}, y), ...
           varargin, num2cell(1:nargin));
       % Construct a single name from multiple strings
       name = deblank(sprintf('%s ', varargin{:}));
   end

Easter Egg

Then, we generate a random number based on the current time (using now) to let us print a special message (Easter Egg) roughly 5% of the time. We use the new-ish rng function which allows us to easily set a single seed for rand, randi, and randn. The seed is generated using the millisecond output from now.

   [~, ~, ~, ~, ~, sec] = datevec(now);
   rng(sec*1000);
   if randi(100) > 95
       fprintf('%s, you are scary enough without a costume!', name);
       return;
   end

Set Random Seed

Then if the special case isn't reached, we generate a new seed for our random number generator. This time, it is based off of the input name and the time (again given by now, but this time we just take the day information). We add up the letters of the name to create a single number, using sum. Note that if the array is a character string, the result is automatically converted to a double.

   seed = sum(name) + floor(now);
   rng(seed);

Create Halloween Name

We are using the work by Steven Savage as inspiration. The Halloween name can have 4 different parts:

  • a creature (like "ghost")
  • a descriptor (like "ghastly")
  • an action (like "floating")
  • a phrase (like "above your head")

You are always a creature, and you have one of the following three configurations:

  • a creature + a descriptor
  • a creature + an action
  • a creature + a descriptor and an action

We use randi to randomly choose between the three configurations. Since the output of the function is 1, 2, or 3, this is a perfect case for switch - case construct. Within each case block, we call randi again (we love that function!) to randomly select the specific entry from the loaded list of descriptors and actions. In the "Action" block, we call randi once again to add a prepositional phrase 50% of the time.

   configID = randi(3);
   switch configID
       case 1 % Descriptor
           <choose a descriptor>
       case 2 % Action
           <choose an action>
           if randi(2) == 1 % Additional phrase
              <choose a phrase>
           end
       case 3 % Descriptor + Action
           <choose a descriptor and an action>
   end

Let's see the whole function. Note that we sorted the Halloween name parts at the beginning, because Sarah likes things in alphabetical order. The creature names were inspired by this web site.

type getName
function halloween_name = getName(data)
% Copyright 2011 The MathWorks, Inc.

% Sort data alphabetically
data.Creatures = sort(data.Creatures);
data.Descriptors = sort(data.Descriptors);
data.Actions = sort(data.Actions);
data.Phrases = sort(data.Phrases);

% Choose a random creature
creatureID = randi(length(data.Creatures));

% Choose one of 3: Descriptor, Action, Descriptor+Action
configID = randi(3);

switch configID
    case 1 % Descriptor
        descriptorID = randi(length(data.Descriptors));
        halloween_name = [data.Descriptors{descriptorID}, ' ', ...
            data.Creatures{creatureID}];
   
    case 2 % Action
        actionID = randi(length(data.Actions));
        halloween_name = [data.Creatures{creatureID}, ' ', ...
            data.Actions{actionID}];
        
        % Additional phrase
        if randi(2) == 1
            rand_phrase = randi(length(data.Phrases));
            halloween_name = [halloween_name, ' ', ...
                data.Phrases{rand_phrase}];
        end
        
    case 3 % Descriptor + Action
        descriptorID = randi(length(data.Descriptors));
        actionID = randi(length(data.Actions));
        halloween_name = [data.Descriptors{descriptorID}, ' ', ...
            data.Creatures{creatureID}, ' ', data.Actions{actionID}];
end

Print Halloween Name

Finally, we print out your Halloween name to the screen. We do a simple grammar check to use the appropriate article based on the first letter of the name.

type printName
function printName(name, halloween_name)
% Copyright 2011 The MathWorks, Inc.

% Check the first letter of the name
if ismember(lower(halloween_name(1)), {'a', 'e', 'i', 'o', 'u'})
    fprintf('%s, for Halloween, you will be an %s.\n', name, halloween_name);
else
    fprintf('%s, for Halloween, you will be a %s.\n', name, halloween_name);
end

The files

Download all the necessary files here to try it out yourself! You can use your own words by creating a MAT file named "HalloweenData.mat" containing four cell arrays: Creatures, Descriptors, Actions, and Phrases.

Fun with RAND

What kind of interesting things have you done using a random number generator? Tell us about it here.




Published with MATLAB® 7.13


  • print