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.