We accomplished quite a bit in parrot-babysteps-06-files-and-hashes.
We figured out how to parse a 20 MB star catalog and search for information that
we thought could be important. The only problem is that it was turning to
spaghetti. Even though it had barely 100 lines of code, it was becoming a bigger
challenge to figure out what was going on or how to add new features.
Today we’re going to streamline the code somewhat by wrapping that complexity
in subroutines. The Parrot Book has a sizable chapter discussing subroutines. I won’t be spending much time exploring the depths of subroutines, because that would take me far beyond what is appropriate for a babystep. However, a quick glance at the chapter should suggest that Parrot subroutines are quite powerful and worth deeper exploration on your own.
Subroutines
We’ve been working with subroutines since parrot-babysteps-01-getting-started Every Parrot application has a subroutine tagged as :main to show that it contains the main logic for the program. Let’s start adding our own supplementary subroutines.
Our first function will encapsulate the display of star highlights.
To create a subroutine that will get used by your :main sub, all you need to do is declare a .sub.
I like my subroutine names to clearly describe the task being accomplished,
to minimize the guesswork when I come back to code later.
This subroutine accepts a single parameter: a Hash describing the star to be
printed. We learned a few steps ago
that the .param directive declares a parameter for your subroutine.
The subroutine body in this case is going to be a copy and paste of the
DISPLAY_STAR_DETAILS code chunk, along with declarations of .local
variables needed to make it work.
We no longer care about stellar distances in our main code, so we can safely
remove the .local string star_distance directive from main.
Now we can rewrite our code to display Sol’s details. Remember that subroutines
require that their parameters be wrapped in parentheses.
We could have also wrapped say_star_details in quotes, but it’s only required
when our subroutines have non-ASCII characters - that is, characters outside the
range of what we consider “normal” characters in the United States. Still, I won’t
complain if you’re devoted to good form and prefer to show those subroutine calls
as:
The DISPLAY_STAR_DETAILS chunk becomes just a few lines:
Does it produce the same result as the code we ran before?
It sure does. The code is still rather awkward, though. How about we add a
subroutine for transforming a line from the text file into star data?
Returning Values
The code is starting to get a little long, so I am adopting the habit of replacing subroutine blocks with # ... when the code is unchanged from the previous example.
Most of the code in our new extract_star_details subroutine looks familiar, but we do have one noteworthy addition:
This directive hands the Hash we’ve just built back to whoever called the function.
Is our application cleaner? Yes, a little bit. I’m tired of having so many unnamed
stars, though. Let’s add a little logic to attempt an alternate name if no proper
name is available.
Making say_star_details Smarter
Now this version of the app displays everything along with some kind of
designation. The order I look for names is arbitrary, and is based
roughly in order of how familiar they looked to me. The tediousness of
determining which reference to use has been hidden away in the say_star_details
subroutine, and consists of simply checking each field for a value until
something useful is found. I knew there would be some kind of name to display,
so I removed the name-counting functionality from main.
Conclusion
Right. Our script has grown to the point where it shows every G2V star
in the HYG database, and some of the complexity of this task has been
tucked away behind subroutines. Is there more to be done? You bet! I would
love to add user search features to the code. That’s going to a fair step
on its own, so I think I will close my Vim window and push this page.