You'll get your Mac news here from now on...

Help TMO Grow

Subscriber Login

Advertising Info


The Mac Observer Express Daily Newsletter


More Info

Site Navigation

Home
News
Tips
Columns & Editorials
Reviews
Reports
Archives
Search
Forums
Links
Mac Links
Software
Reports
Contact



by Stephen Swift


Break Your Script Into Pieces with Subroutines
June 26th, 2002

This marks the 10th installment of AppleScript: The Macintosh Autopilot. If you find any of this tutorial not making sense, check out the some of the earlier installments.

Download today's script files! Decompress the file, open the folder, and drop these scripts onto the script editor. And now onto today's lesson!

icon Download Script Examples
(Link corrected)

When writing an essay, we break our ideas up into paragraphs each focusing on one idea. This makes the essay easy to read and understand. Programmers do the same thing with their code using subroutines. An essay consisting of one huge paragraph is not the most elegant paper. One large chunk of code is not the most elegant script. In this article, I will demonstrate how to organize your code using subroutines.

The Basic Subroutine

on subroutine_name()
--do action here
return some_value
end subroutine_name

You can name your subroutine whatever you want (normally something that makes sense). You enclose your subroutine like a regular statement with on and end commands. You can have your subroutine do any action that a regular script could do. The only difference is, the subroutine must return a value. You can either return a value using the return command, or you can omit the return command and the subroutine will return the last value by default. I will talk about the parentheses later.

To use a subroutine, you use the first line of the subroutine but omit the word 'on'. Essentially, you call a subroutine by typing its name followed by the parentheses. For example, this subroutine would be called with this code:

subroutine_name()

Here is an example:

SimpleSubroutine()
--use the subroutine (programmers say call subroutine)

on SimpleSubroutine()
return "hello"
--Run this script and look at the result window
end SimpleSubroutine
--The subroutine called. It can be anywhere in the script.

As I mentioned before, the subroutine returns a value. Therefore, we can treat our subroutine like any other type of data. If the value is a string, we can put in a dialog box.

on SimpleSubroutine()
return "hello"
end SimpleSubroutine

display dialog SimpleSubroutine()
--This line is equivalent to display dialog "hello"

We can set it to a variable.

on SimpleSubroutine()
return 5
end SimpleSubroutine

set x to SimpleSubroutine()
x > 10
--> false (Run this script and look at the result window)

Of course, most of the time, subroutines are more complex than just a few lines of code, but these simple subroutines help demonstrate what subroutines can do.

Those Empty Parentheses

The empty parentheses are very useful. Try this script:

set x to 5

SimpleSubroutine()

on SimpleSubroutine()
x > 10
end SimpleSubroutine

You got the error message; "The variable x is not defined," didn't you? That's because variables loose their values when they enter a subroutine. Think of a subroutine as a new script almost. Variables need to be redefined in new scripts. However, we know of a special variable called a property that doesn't need to be redefined each time a script is run. Properties can also be used in subroutines. Try this script:

property x : 5

SimpleSubroutine()

on SimpleSubroutine()
x > 10
--false
end SimpleSubroutine

However, remember that properties are supposed to be used sparingly (like chili powder). We can use regular variables in subroutines, by placing them in the parentheses. Try this script:

set x to 5

SimpleSubroutine(x)

on SimpleSubroutine(x)
x > 10
--false
end SimpleSubroutine

TIP: Variables don't even have to have the same name between subroutines and the regular script. If I wanted, I could set x to 5 outside my subroutine and then decide to use the variable num inside my subroutine. For example:

set x to 5

SimpleSubroutine(x)

on SimpleSubroutine(num)
--it's the same variable just with a new name!
num > 10
--false
end SimpleSubroutine

Recursive Subroutines (Or: Why Subroutines Are Useful)

Granted, you could just divide your script up by placing comments between each of your script sections, but subroutines are used all the time, so it helps if you can recognize them and be able to use them. Also, the recursive subroutines are extremely useful.

A recursive subroutine is a subroutine that needs to be called multiple times in one script. Say you had a subroutine of 10 lines that you called 5 times. If you didn't use the subroutine, you would have to type out 50 lines of code instead of 10.

A variation on the recursive subroutine is the infinite subroutine. Sometime in your scripting career, you will need an action to call upon itself. The only possible way of doing this is using a subroutine. For example, say you had a dialog box with two options. Option 1 displayed a new dialog box. Option 2 displayed the exact same dialog box. Sounds easy right? It's not because, you will be writing code forever. Here is an example that allows option 2 to be selected three times. On the fourth time, the script just stops.

display dialog "Click Button 1 for a different dialog box. Click Button 2 for the same dialog box." buttons ["Button 1", "Button 2"] default button 1
if button returned of result = "Button 1" then
display dialog "This is a new dialog box." buttons ["OK"] default button 1
else
--create the original dialog
display dialog "Click Button 1 for a different dialog box. Click Button 2 for the same dialog box." buttons ["Button 1", "Button 2"] default button 1
if button returned of result = "Button 1" then
display dialog "This is a new dialog box." buttons ["OK"] default button 1
else
--create the original dialog
display dialog "Click Button 1 for a different dialog box. Click Button 2 for the same dialog box." buttons ["Button 1", "Button 2"] default button 1
if button returned of result = "Button 1" then
display dialog "This is a new dialog box." buttons ["OK"] default button 1
else
--create the original dialog
display dialog "Click Button 1 for a different dialog box. Click Button 2 for the same dialog box." buttons ["Button 1", "Button 2"] default button 1
if button returned of result = "Button 1" then
display dialog "This is a new dialog box." buttons ["OK"] default button 1
--too much code!
end if
end if
end if
end if


Each time option 2 is selected; you have to write the exact same script that created the first dialog box. At some point, you will have to stop. However, we can use a subroutine. Subroutines are great ways to prevent code from being repeated.

Here's the original dialog box:

display dialog "Click Button 1 for a different dialog box. Click Button 2 for the same dialog box." buttons ["Button 1", "Button 2"] default button 1

The user can make a choice, so we need an if-then statement.

if button returned of result = "Button 1" then
--if the user selects button 1
display dialog "This is a new dialog box." buttons ["OK"] default button 1
-- then display a new dialog box
end if

Now let's enclose our script in a subroutine.

on DialogBoxes()
display dialog "Click Button 1 for a different dialog box. Click Button 2 for the same dialog box." buttons ["Button 1", "Button 2"] default button 1
if button returned of result = "Button 1" then
--if the user selects button 1
display dialog "This is a new dialog box." buttons ["OK"] default button 1
-- then display a new dialog box
end if
--so far, nothing happens if button 2 is pressed
end DialogBoxes

To run the subroutine, we must call it:

DialogBoxes()

If button 2 is pressed, we want the dialog box to be displayed again, so we need to call the subroutine that we just created. Luckily, subroutines can be called from within themselves. So we need to add

else
DialogBoxes()

to our if-then statement. Now our script is complete:

DialogBoxes()

on DialogBoxes()
display dialog "Click Button 1 for a different dialog box. Click Button 2 for the same dialog box." buttons ["Button 1", "Button 2"] default button 1
if button returned of result = "Button 1" then
--if the user selects button 1
display dialog "This is a new dialog box." buttons ["OK"] default button 1
-- then display a new dialog box
else
DialogBoxes()
--if the user selects button 2, run this subroutine again
end if
end DialogBoxes

Would you ever need to call the same dialog box from itself? Probably not. Are there times where you will have to call a subroutine from within itself? Definitely.

Silly Subroutine Tricks

Let's say we have two subroutines:

on choice1()
return "choice one"
--Look at the result window
end choice1
on choice2()
return "choice two"
--Look at the result window
end choice2

What if we only wanted to call one of these subroutines, but the one we wanted to call wasn't always the same one? We can set subroutines to other subroutines.

set theList to {choice1, choice2}
--make a list of all your choices
set RunHand to item 1 of theList
--set a variable to one of the choices (in the case, choice1)
RunHand()
--call the handler. This is equivalent to typing choice1().

To have the user select which subroutine they want to call, make a list.

set someList to {1, 2, 3, 4, 5}
--make a list with numbers
set theChoice to choose from list someList
--thechoice = the number the user selected
set theList to {choice1, choice2, choice3, choice4, choice5}
--a list of all your choices
set RunHand to item theChoice of theList
--selects the choice with the same number that the user selected
RunHand()
--call the handler.
on choice1()
return "choice one"
--Look at the result window
end choice1
on choice2()
return "choice two"
--Look at the result window
end choice2
on choice3()
return "choice three"
--Look at the result window
end choice3
on choice4()
return "choice four"
--Look at the result window
end choice4
on choice5()
return "choice five"
--Look at the result window
end choice5

Run this script. The number you selected runs a specific subroutine and displays text in the result window. Why is this useful? It replaces a long string of if-then statements. If we didn't use subroutines, the script would look like this:

set someList to {1, 2, 3, 4, 5}
--make a list with numbers
set theChoice to choose from list someList
if theChoice = {1} then return "choice one"
if theChoice = {2} then return "choice two"
if theChoice = {3} then return "choice three"
if theChoice = {4} then return "choice four"
if theChoice = {5} then return "choice five"

Now there's nothing wrong with this really; both methods take about the same amount of time to complete, and it's really a matter of preference. I personally am not a fan of the nested if-then statements. I would rather use subroutines; however, if you are more comfortable using nested if-then statements, go ahead. I am just trying to broaden your horizon.

The Last Word

Now you can break your scripts into little pieces to organize your scripts, to cut down on code, and to make them more readable. There are many different types of subroutines and different ways of calling and identifying them. For now, the simplest way is the best way.

Learning how to create subroutines completes the AppleScript Basic Training course. In these articles we learned how AppleScript works (by sending messages called AppleEvents between applications on your computer), how to make decisions using the if-then statements, how to create an interface for your programs using dialog boxes, how to store data in variables, how to repeat monotonous tasks using repeat loops, and how to organize scripts using subroutines. Hopefully, you will now be able to create your own basic scripts using these techniques.

AppleScript has much more to offer, however. In upcoming articles, I'll show you how to master AppleScript using new techniques, how to extend AppleScript's functionality, and show you where AppleScript really shines performing some really cool tasks.

More Info

Join the AppleScript gang at the Coder's Corner in The Mac Observer Forums! We discuss AppleScript related news and updates, extract really cool and useful scripts, ask questions, and give answers. It's a lot of fun and a great resource for all your scripting needs! Also, if you have a perplexing AppleScript problem, please send it to Stephen@macobserver.com and I'll be more than willing to help you out!


Comments or Questions? Is this column going to slow or fast for you? Do you want to script something, but don't know how? Do you need something explained or have a question about a script?  My E-mail address, stephen@macobserver.com, is open 24 hours a day, 7 days a week.


Most Recent AppleScript Columns

Break Your Script Into Pieces with Subroutines
June 26th

Repeat Yourself With AppleScript
June 12th

AppleScript Lists
June 5th

AppleScript Archives

Back to The Mac Observer For More Mac News!



Today's Mac Headlines

[Podcast]Podcast - Apple Weekly Report #135: Apple Lawsuits, Banned iPhone Ad, Green MacBook Ad

We also offer Today's News On One Page!

Yesterday's News

 

[Podcast]Podcast - Mac Geek Gab #178: Batch Permission Changes, Encrypting Follow-up, Re-Enabling AirPort, and GigE speeds

We also offer Yesterday's News On One Page!

Mac Products Guide
New Arrivals
New and updated products added to the Guide.

Hot Deals
Great prices on hot selling Mac products from your favorite Macintosh resellers.

Special Offers
Promotions and offers direct from Macintosh developers and magazines.

Software
Browse the software section for over 17,000 Macintosh applications and software titles.

Hardware
Over 4,000 peripherals and accessories such as cameras, printers, scanners, keyboards, mice and more.

© All information presented on this site is copyrighted by The Mac Observer except where otherwise noted. No portion of this site may be copied without express written consent. Other sites are invited to link to any aspect of this site provided that all content is presented in its original form and is not placed within another .