Troubleshooters.Com
Presents
Linux Productivity
Magazine
Volume
2 Issue 11, November 2003
Perl
|
Copyright (C) 2003 by Steve Litt. All rights reserved.
Materials from guest authors copyrighted by them and licensed for perpetual
use to Linux Productivity Magazine. All rights reserved to the copyright holder,
except for items specifically marked otherwise (certain free software source
code, GNU/GPL, etc.). All material herein provided "As-Is". User assumes all
risk and responsibility for any outcome.
[ Troubleshooters.Com
| Back Issues |Troubleshooting Professional Magazine
]
Perl is humble. It doesn't try to tell
the programmer how to program. It lets the programmer decide what rules today,
and what sucks. It doesn't have any theoretical axes to grind. -- Larry Wall (from Perl, the first
postmodern computer language)
|
CONTENTS
Editor's Desk
By Steve Litt
Use of Linux doesn't require genius anymore than use of Windows requires stupidity.
These widespread perceptions are false.
That being said, the Windows Culture encourages stupidity. It's how they foster
the dependence necessary to maintain a monopoly. It's how they differentiate
themselves from UNIX type operating systems, which are easy to configure and
customize with no more than a text editor.
Fact is, people computed just fine before Windows. Legal secretaries used
menus and commands to do their work in DOS, and occasionally, when tech support
was too slow in arriving, they fixed operating systems. The old question,
"can my grandmother use it", is misleading. Grandma has been brainwashed,
by those who find it profitable to so brainwash, that she cannot perform any
task which isn't fully prompted with clicks and drags. Grandma has no idea
of her capabilities.
The Linux culture encourages intelligence. Users are encouraged to choose
the best desktop environment, the best browser, the best word processor, the
best email client, from among many. Users are encouraged to configure these
programs to suit their work habits and needs. If Windows is a culture of
dependence, Linux is a declaration of independence. Use Linux however you
want. And spread the word.
So Linux grows.
Young or old, smart or dumb, male or female, the person long exposed to the
Linux culture soon finds that he or she has the brainpower to take the next
step -- writing computer programs to make his or her work easier. Many fine
languages abound on the Linux platform: C, C++, Java, Python, Ruby, and Perl.
Each has its advantages and disadvantages.
This month's Linux Productivity Magazine discusses one of those languages,
Perl. The technical guts of Perl aren't discussed, because Troubleshooters.Com
features the excellent Litt's Perls of Wisdom site, which explains most of
what you need to know about Perl syntax and usage. Instead, this month's LPM
issue discusses the thinking that goes into choosing Perl as a language, and
the thinking that goes into writing a Perl program.
So kick back, relax, and enjoy. If you use Linux or free software, this is
your magazine.
Help Publicize
Linux Productivity Magazine
By Steve Litt
Loyal readers, I need your help.
For months I've publicized Linux Productivity Magazine, expanding it from
a new magazine to a mainstay read by thousands. There's a limit to what I
can do alone, but if you take one minute to help, the possibilities are boundless.
If you like this magazine, please report it to one of the Linux magazines.
Tell them the URL, why you like it, and ask them to link to it.
I report it to them, but they don't take it very seriously when an author
blows his own horn. When a hundred readers report the magazine, they'll sit
up and take notice.
Reporting is simple enough. Just click on one of these links, and report the
magazine. It will take less than 5 minutes.
If you really like this magazine, please take 5 minutes to help bring it to
a wider audience. Submit it to one of the preceding sites.
Comparative Religion
By Steve Litt
Perl is the language I use most often. I like it very much, and unless there's
a strong reason to the contrary, I use it exclusively. In this article I'll
tell you why, and also give you information with which you can choose your
own default language.
There's an old saying: "jack of all trades, master of none". Like most programmers,
I've had many 4 language days -- days when I coded in 4 different languages.
It's often necessary. However, the more you focus on a single language, the
better you get at that language, and the more you can accomplish in less time.
For that reason, I suggest you pick a "default language" -- that is, a language
which you use for everything unless there's a very strong reason to use something
else.
My default language is Perl. It's installed on almost all UNIX, Linux and
BSD machines, so I really can "write once, run anywhere". It's available no
cost or low cost (like $39.00) for Windows from ActiveState, and also Open
Source from Siemens as the SiePerl distro. Due to its built in regular expressions,
weak typing, and numerous shortcuts, it develops lightning fast. The debugging
phase takes longer than I'd like, but I can live with that. Unlike many languages
bundled with Linux, Perl expertise actually leads to paid work.
These are my opinions.
Programming language discussions are like religious discussions -- lively,
forceful and opinionated. This article discusses C, C++, Java, Python, Ruby,
TCL, QT, gTk, bash and Perl. In my opinion these are all excellent languages.
I've programmed in bad languages in the past, so I know a dog when I see one,
and none of these is a dog.
I've programmed hundreds of thousand lines of C, C++ and Perl, tens of thousands
of lines of Java and Python, and thousands of lines of bash shellscirpts.
I have a passing knowledge of Ruby because it's similar to C++, Java, and
Python. I know little of gTK and QT, and have only a passing familiarity with
TCL.
The following is a brief comparison of the languages mentioned:
Language
|
Description
|
Advantages
|
Disadvantages
|
Availability
|
C
|
A slim, trim, machine level compiler.
|
- Creates fast runtime programs.
- Complete access to hardware, memory and I/O.
- Strict type checking helps prevent errors.
|
- The language has few high level constructs, so development is
relatively slow.
- Heavy use of memory pointers, and necessity of the programmer
managing the memory, often leads to intermittents and bugs.
- Strict type checking slows development.
|
Installed by default on all UNIX/Linux/BSD machines.
Can be purchased for Windows. |
C++
|
C + OOP
|
- All advantages of C, although not quite as fast runtime.
- Classes and objects make programs more scalable.
- Strict type checking helps prevent errors.
- Full object encapsulation makes for rock solid programs, except for
pointer problems and buffer overflow.
|
- The same disadvantages as C.
- Strict type checking slows development.
|
Available for most UNIX/Linux/BSD machines, and installed
by default on Linux machines. Can be purchased for Windows. |
Java
|
A machine independent language compiling to language
specific bytecodes. Built from the ground up to be object oriented.
|
- Scalable to huge projects due to innate OOP.
- Built-in memory garbage collection and lack of pointers minimize
bugs.
- The debugging phase of Java programs is incredably fast.
- Strict type checking helps prevent errors.
- Full object encapsulation makes for rock solid programs.
- Huge market for Java programmers.
|
- Runtime performance can be very slow.
- Even simple programs require OOP, meaning simple programs aren't
as simple as you'd like.
- Strict type checking slows development.
|
Available on all major platforms.
|
Python
|
A widely ported interpreter well integrated with OOP.
|
- Well integrated OOP makes Python scalable to fairly large projects.
- Built-in memory garbage collection and lack of pointers minimize
bugs.
- Loose type checking makes for faster development.
- Object encapsulation is available if you know how (__variable),
so you can write pretty solid programs.
- Python's indent sensitive nesting makes it the most readable language
on the planet.
|
- Loose type checking encourages subtle bugs.
- Runtime performance cannot match C or C++.
- There's almost no market for Python programmers.
|
Available on UNIX, Linux, BSD and Windows. Packaged
with most Linux distributions, but often not installed.
|
Ruby
|
An interpreter built from the bottom up with OOP in
mind. Somewhat similar to Perl and Python.
|
- Well integrated OOP makes Ruby scalable to large projects.
- Built-in memory garbage collection and lack of pointers minimize
bugs.
- Loose type checking makes for faster development.
- Object encapsulation makes for solid programs.
|
- Runtime performance cannot match C or C++.
- There is zero market for Ruby programmers.
|
Available, but seldom installed, on Linux. On other
platforms one can compile Ruby from scratch and install.
|
TCL
|
An interpreter whose syntax and use is very different
from C or the other languages discussed here. Those who know how can write
substantial programs with just a few lines of TCL, but it's not very scalable.
|
- Substantial programs can be made with just a few TCL statements.
|
- No OOP, and not very scalable.
- TCL is seldom used, and there's little market for TCL programmers.
|
Bundled with, and usually installed on Linux. Available
for other platforms, including Windows.
|
QT
|
A compiled language designed to create graphical applications.
|
- Creates fast and efficient graphical applications.
- There are a few QT jobs out there.
|
|
Bundled with Linux, available via compilation on most
other platforms.
|
gTk
|
A compiled language designed to create graphical applications. |
- Creates fast and efficient graphical applications.
- There are a few gTk jobs out there.
|
|
Bundled with Linux, available via compilation on most
other platforms. |
bash |
A command interpreter that can be used as a language. |
- Outstandingly modularity is achieved with separate executables,
piping and redirection.
- Quality control is easy if you assemble the program from tested,
known good executables.
- Non-looping performance is as fast as the optimized programs it
calls.
- Available from very early in bootup, so bash scripts can be used
to control bootup.
|
- Looping performance is unacceptably slow.
- The syntax is maddeningly quirky.
- No OOP.
- Little support for complex data constructs
|
Installed on all Linux boxes. Bash, or something very
similar, is installed on almost all UNIX and BSD boxes. Can only be achieved
on Windows with mating products such as
|
Perl
NOTE: This evaluation is for Perl 5.
|
An interpreter optimized to do big things with few
lines. OOP has been tacked on after the fact.
|
- Availability of OOP makes Perl scalable to moderate sized projects.
- Built-in memory garbage collection and lack of pointers minimize
bugs.
- Loose type checking makes for faster development.
- Used ubiquitously throughout the UNIX/Linux/BSD world.
- A Perl program is likely to run on any target machine.
- Substantial market for Perl skills.
- Very fast for an interpreter.
- The CPAN Perl module repository contains tools to simplify almost
anything you need to do.
|
- Lack of encapsulation and private variables can lead to bugs in
larger projects.
- The "many ways to perform a task" philosophy of Perl leads to
unreadable code and difficult debugging, and a lack of "best practices".
- Lack of "best practices" leads to some mighty bad Perl code out
there.
|
Installed by default on all Linux, BSD and Unix machines.
Available for Windows and all other platforms.
|
One chooses a language to meet the need at hand. That being said, it's best
to choose a default language to use in on those occasions when you don't
have strong reasons to do use a different language. The default language
I've chosen is Perl.
Perl's not perfect. It's not even the language I like the best. I prefer Python.
But I choose Perl over Python for the following reasons:
- Perl's most likely to be installed on the majority of machines.
- Perl's CPAN library is one of the most complete, and can be used to
accomplish almost anything in Perl.
- There's actually paid work to be done in Perl.
Many people like Ruby because it's OOP from the ground up, and it's a powerful
language. However, the same three reasons I choose Perl over Python apply
doubly to Ruby.
The choice of Perl over Python and Ruby is a somewhat difficult case to make.
Choosing Perl over C, C++ and Java is much more supportable.
C and C++ require the programmer to delete allocated memory, and they depend
on memory pointers. These two factors almost guarantee bugs in any C or C++
program. I mean bugs that survive the debugging stage of development and are
seen by the user. In many cases, these bugs are exploitable by crackers as
buffer overrun errors. Also, C and C++ provide little in the way of higher
level constructs, so the programmer is forced to build each app from scratch.
Perl, especially fortified with the CPAN repository, enables very rapid application
development.
Then there's Java. Java's a wonderful language, with Perl's advantages of
memory management and lack of pointers. Java is such an organized language
that it almost debugs itself. The debugging phase of Java is MUCH quicker
than that of Perl. But oh, the coding phase! Java is huge. There's tons to
be learned. And it's OOP only, which means that even in problem domains not
resembling objects, one must still use objects. Hello World requires OOP code.
And then there's the matter of Java's performance. It's getting better, but
it's still slow.
gTk and QT are just too complicated for me to spend time learning. Bash is
great as long as the program stays under 100 lines. TCL is just too different
-- I'm used to the Pascal derived languages -- C, C++, Java, Perl, Python,
Ruby, VB, etc. Give me Lisp, Prolog, or yes, TCL, and I'm a newbie again.
How to Choose Your Default Language
What are your priorities? Are you writing mostly device drivers? If so, you
need C. Are quick development and freedom from bugs your priority? Stay away
from C and C++. Is your main priority getting a job? I've got one word for
you -- Java! Or C# if you swing that way. Do you want the most readable language
ever created, and get great OOP, quick development and freedom from bugs
to boot? Python! Do you want an OOP from the ground up language? Java or
Ruby. Do you want the most ubiquitous interpreter in the UNIX world, with
quick development and freedom from memory bugs? Perl.
I'd caution you that it can be heartbreaking to work with a language nobody
else uses. I have a buddy who works wonders with a language called REBOL.
He talks about it continuously and shows off his work on a regular basis.
It does no good -- REBOL costs money so we all use C, Java, Perl, Python and
Ruby. He has no community from which to garner further knowledge. My friend
walks this world alone. There's something to be said for working in a common
language, even if it's not ideal.
Perl as an Admin's Language
By Steve Litt
The first step in the evolution from user to power user is the use of scripts
to accomplish specific tasks. Most of us start with shell scripts. In DOS
and Windows we used "batch files". In UNIX and its derivatives and workalikes
we use shellscripts. Shellscripts are great because, when they accomplish
simple things, they're simple to write. But as the task at hand gets more
complex, shellscripts become ever more cumbersome.
The next step is to use an interpreter to accomplish a task. Perl, Python,
and Ruby are examples. Perl is wonderful because it's installed and running
on virtually all UNIX and UNIX workalike systems (i.e. Linux).
Perl as a Glue Language
A glue language is a language used to "glue" various other executables together.
The ultimate glue language, of course, is bash (shellscripts in general).
Perl can be used to call other executables, just like a shellscript. To call
another executable, use the backtick operator to make the call another program
and obtain its output, and use a manipulation of the $? variable to obtain
the child process's return value (but only if it returns in the range of -127
through 128). The following code snippet shows this:
my @output = `./child.pl`; foreach my $line (@output) { chomp($line); print "$line\n"; } my $childexit = ($? <= 32768 ? $? >> 8 : 0 - ((65536 - $?) >> 8)); print "\nReturn value=>$childexit<\n";
|
Shellscripts can do that quite easily, but Perl brings other assets to the
table:
- Fast loops
- Reasonable syntax
- OOP
- Full programming language logic
- Interfaces to many filetypes and protocols via CPAN modules
Shellscripts evaluate loops via the test program, which instantiates very
slowly. Hence shellscript loops are incredibly slow. Perl loops are fast.
Perl has a much cleaner syntax than bash (almost anything has a cleaner syntax
than bash). Perl has full logic, OOP, and interfaces without number.
So if you need to run several processes, and you need logic to decide which
processes and how to run them, glue it all together with Perl.
Perl as a File Conversion Language
One way admins use Perl is as a file conversion program. Its simple and efficient
file I/O, loops and regular expressions make Perl a natural for this usage.
If the perl program is used as a filter, meaning it takes its input from stdin
and places its output in stdout, just a few lines of Perl can effect complex
file transformations in less than 10 lines of code. While it's true that
AWK can be used the same way, few people know AWK, and Perl can do anything
AWK can do.
Perl as a Reporting Language
There are two kinds of reports -- cutesy reports with lots of graphics, whitespace
and fonts, and reports showing the facts. The first type typically requires
lots of programming with tools like Crystal Reports or whatever corporationally
correct report generator they're using this month. The latter type requires
a few minutes of Perl programming.
Perl's built in regular expressions remove the drudgery, errors and debugging
from text parsing. If you've ever tried to parse text with straight C you
know what I mean. Beyond that, Perl makes it easy to develop data structures
that match the input and output of the program, so you can do it all in memory,
typically in a single pass. Perl manages memory for you, so you needn't worry
about allocating and freeing memory, and you needn't worry about the effect
of local variables on the stack. Stacks, linked lists, binary trees, node
trees, arrays, hashes (name->value pairs), and many other data constructs
are easily implemented. Oftentimes objects speed your work. For instance,
you could create an object to represent the piece of paper, lay out the output
there, and when it's full call its print function.
Perl has complete database access via the DBI::DBD interface, so any common
DBMS is readable.
Perl for Web Apps
Perl is used extensively to create web apps. At one time it was the most common
web programming language, although within the last few years ASP, .NET, PHP,
Zope and others have overtaken it. But it's still excellent for a quick and
dirty web app, and if you take the time to devise and/or download some webapp
specific Perl modules, Perl can be a highly productive web app environment.
Perl has all the tools:
- Database connectivity, read and write, through DBI::DBD
- Ability to write web pages via the CGI interface
- Full programming logic
- OOP
- Enhanced performance and security through Apache's mod_perl
Let's say your newly purchased in-house accounting system requires 18 mouseclicks
and 4 field entries on 8 screens to change a certain variable that should
be changeable by itself. The users are revolting. The outsourcers who created
the app with cheap offshore talent will get to it "real soon now".
You tell the boss you can take the pressure off, and he says go for it. You
take a couple hours to write a program that manages a single screen interfacing
with the correct table, using Perl's DBI::DBD interface. You take another
few hours testing it, show it to the boss, and by the end of the day every
user knows the URL to save 10 minutes work by using this interface.
The users love you -- you save them time and frustration. The boss loves you.
You saved him a lot of trouble. The outsourcers love you. You saved them
from an embarrassing situation and bought them lots of time.
In smaller organizations, quickie web apps often morph into full fledged apps
over time. The ability to make a prototype in a day often makes the difference
between acceptance and rejection of an idea. From there on, you're given
time to develop something really nice. It wouldn't be the first time a fulltime
job resulted from a one day app.
Summary
Computer administrators have been using Perl for adminstration since the dawn
of time. Perl was written by Larry Wall specifically for computer administration.
Perl gives administrators 20 inch biceps. But it can be used for more than
just administration. Read on...
Adding Functionalities
with Perl
By Steve Litt
Sometimes users require a brand new functionality on their computers. Perl
is usually the fastest way to give them the exact functionality they need.
Here at Troubleshooters.Com, I needed a keyboard friendly menu system that
would work on a console as well as in a graphical environment. In the DOS
world, I had written a couple such programs in C, and it was difficult to
say the least. So this time I used Perl. I gave myself 2 days to do it.
The result was UMENU. You can download it from the Troubleshooters.Com site.
I use UMENU over 100 times a day. It's perfect.
When I switched to Linux, I needed an outline processor that would run on
Linux. The outliners running on Linux at the time were either underdeveloped,
stalled in development, or too cumbersome. I adapted the Vim editor to serve
as an outliner.
Vim couldn't do the job alone. Inter-outline linking required a method of
traversing trees of outlines and writing a tag file. Modularity required several
environment variables be set before running Vim. Conversions were needed
to convert the outline to HTML, LyX, and other formats. To get all this done
quickly, I used Perl. A few days later I had a great outliner called VimOutliner,
and over the years others downloaded it and it became a project.
An interesting thing happened. The current VimOutliner developers code mostly
in Vim language, with some python, ruby and bash thrown in for good measure.
Very little Perl survives. But it was my knowledge and skill with Perl that
allowed VimOutliner to be born, instead of spending eternity as a "might work
some day" project like most of the other Linux outliners existing in 2001.
In a matter of 2 weeks VimOutliner went from a wish to a fully functional
outliner, leaving the others in its dust and garnering a stable of high power
developers. Perl was VimOutliner's jumpstart.
Perl is an EXCELLENT application development language. Let me repeat that:
Perl is an EXCELLENT application development language. For quick prototypes,
it has few peers. I was once hired to create a Perl web app to interface to
multiple routers and switches across the nation over the Internet via the
SMNP protocol. Now the interesting thing was, this company had a crew of
several developers working on a Java version of the same functionality, but
they needed it quicker. So I came in and coded it up in a week, using Perl.
For my client, it was money well spent. I discovered many opportunities and
pitfalls, passed them on to the Java developers, and saved them lots of time.
On the expense side alone, it was probably cheaper to hire me to do the Perl
app than not to. And of course, my quick prototype allowed them to start
collecting revenue several weeks earlier.
Many times, quick prototype impress to the extent that they morph into full
blown apps. Perl is an excellent language for full blown apps. It has OOP,
including inheritance. While the OOP lacks the encapsulation necessary for
huge apps (>100,000 lines), it's perfect for large apps created by 1 to
5 developers. Perl's memory management and lack of pointers mean that finished
apps are remarkably bug free, and those bugs that make it into the finished
products are usually neither subtle, hard to reproduce, nor hard to attribute
to a root cause.
Whatever you're doing, it's probably been done before. In Perl. Therefore,
it's likely you'll find the tools you need on CPAN. If so, you can cut development
time severalfold.
Designing Perl Programs
By Steve Litt
Design is everything. It makes the difference between a working prototype
tonight, or a tangled mess next week. It makes the difference between a scalable
and maintainable app, or an albatross nobody (including you) wants to touch.
This is true in any computer language or development environment. There's
no substitute for design. The question is, what design method should you use?
Functional Decomposition for Simple Apps
The first question to ask is whether this will be a simple app. If the app
is simple -- the kind of thing that will take a few hours -- the fastest
design method is good old functional decomposition, AKA structured programming,
modular programming, top down programming, 80's programming, etc. For added
design speed, use an outline processor. VimOutliner is an excellent outline
processor.
Once you've broken down the task at hand into subroutines calling other subroutines
etc, ask yourself 2 questions:
- Does any of the subroutines need to keep lots of state information?
- Does the problem domain include entities requiring a lot of information?
If either is true to a great enough extent that a pure structured program
would be difficult or convoluted, add objects to help whip it into shape.
The overall program still isn't OOP, but you have a few objects to keep state
and modularity. With any luck you'll have a finished and tested app today
or tomorrow.
Bigger Apps Require More Consideration
If the app can't be written in a day, the functional decomposition shortcut
will likely cost you time, and possibly require a rewrite. Spend a little
more time at the drawing board. Obviously, your first question should be "what
do I want the program to do?". A simple sentence should do. The very next
question you should ask yourself is this:
What data constructs would cleanly represent what I need to do?
Determine the data
For instance, if you're modeling a system of physical objects, like astral
bodies or a shot cannonball, you want to represent each body with an object.
But if you tried to represent an office automation program the same way, your
design would take a long time, and the resulting program would be suboptimal.
With an office automation program, you might start with lists of entities
-- employees, customers, vendors. Those become database tables. Then perhaps
if the office automation program includes accounting, a table of accounts
and a table of transactions.
Perhaps your input or output data is hierarchical -- an outline, or a directory
tree, or an org chart. In that case your data might best be represented by
a tree of nodes, or a self-referential database table.
Don't forget that the program's configuration and parameters are data -- usually
best represented by an object.
Once you've decided the data structure best suited to represent what you need
to do, determine the exact data needed, and its relation to other data. An
outline processor is a convenient way to organize your thoughts, and place
the data elements into the correct objects, database tables, or points on
a hierarchy.
Each data element should have a set() method or function, a get()
method or function, and a has() method or function. The has()
method or function must always be called prior to get() so that you never
operate on a null data item.
Think about the ways the data will be stored between program runs or sessions,
as well as how it will be stored internally during program execution. Will
the data be stored in a relational database, a configuration file, an outline,
or some other way? In the program, will you use the data stored on disk, or
will you cache it in memory for faster execution? Keep in mind that in a
single program different data could be stored in different ways.
Determine the rules
Your next design step is probably to determine the rules. Each piece of data
has rules. Certainly one rule is to determine what you'll do if the data is
missing. Will it be considered blank, zero, or an error? If an error, will
you abort the program immediately, or continue in an intelligent way?
Each piece of data is constructed from inputs. What are the rules for each
piece of data construction? Is there a formula? Are there different formulae
for different situations? Are there defaults that can be overridden?
To the extent possible, try to keep the rules in data rather than the program's
code. This makes the program much more configurable and maintainable.
There are many ways to keep rules in data. With Perl, you can use the eval()
function to treat a string as program code. Thus, you could place theis string
in data:
my $string = '$tax = (isFood($product) | isMedicine($product) ? 0.06 * price : 0);';
And anywhere in the program you could use it like this:
my $tax;
my $product = 19;
my $price = 1.00;
eval $string;
print "Price for product $product is $price, tax is $tax.\n";
In the preceding, you'd also need to define functions isFood($)and
$isMedicine($). This way, you could change the tax formula by changing
the data. If the government started taxing medicine, you could eliminate the$isMedicine()from
the string. If the tax rate went up to 7%, you could change the 0.06to
0.07.
You could also keep subroutine names in data, and for different situations
call different subroutines, once again with an $eval() statement.
That way you call different subroutines (or different objects or whatever)
depending on a data lookup, which itself depends on a criteria. The result
is that you implement complex logic without if statements -- a great
simplifier.
Sometimes simply keeping constants in data is enough. Consider a program to
calculate tax rate by zip code.
Determine all the rules, and find ways to keep as much of those rules as possible
in data, rather than in program code.
Define the Data
By now you know the needed data structure, the rules, and the way you map
those rules into data. Now lay out the data, organizing it carefully by tables,
objects, categories, entities, configuration, and however else you can think
of. Once again, an outline processor provides you with a quick way to arrange
and rearrange your data layout.
Once your data is laid out, to the extent appropriate, map the data into objects.
Code the Data and Object Structure
Now that your data and associated objects/classes are defined, place them
in code. Decide on naming conventions, or use the company's standard naming
conventions, and use a high power editor like Vim to convert your design to
code. Remember, every data item in an object should have set(),get(),
and has() methods.
Code Object Methods
Next, code any other methods that are required to implement rules and calculations.
Hopefully, most of the rules and calculations are contained in stored data,
but to the extent that they're not, code them in methods. Code error detection
and handling in the data methods.
Code the App
Finally, write the code that ties all the data together. Perhaps it's a single
controller object. Perhaps it's structured code. Whatever it is, code the
app. You may need to change the code for the data -- that's OK. The fact that
you designed the data so carefully should prevent dead ends and rewrites.
Summary
Perl is a rapid application development environment, meaning it won't slow
you down on simple apps. Simple apps can be rapidly designed using functional
decomposition, so that's what you should use.
More complex apps require more time consuming design methods. There are many
design methods, but for Perl probably a data-first design method is the most
practical and productive.
Be sure to do adequate design. The time saved by skimping on design time is
invariably consumed in the coding phase, then consumed doubly in the debugging
phase, and then you are rewarded with a cumbersome, hard to maintain app.
Design well.
Mercenary Perl
By Steve Litt
I have good news and bad news. Let's start with the bad news...
The Bad News
These are not good times for programmers (developers, whatever you want to
call them). This world has countries where you can live quite comfortably
for $8,000.00 per year, and those countries house many programmers. These
countries have little labor protection -- no OSHA, no stress leave, no maternity
leave, no workers comp. If such workers develop ulcers due to 90 hour workweeks
and slavedriver bosses, they're discarded like tin cans. Naturally, it's much
cheaper to employ these cheap, abuseable and dispensable foreign workers to
write code.
We're in a recession right now, so like any other recession, programmers are
out of work. But with the new trend toward offshoring, whole development shops
are emptying out and turning out the lights. These jobs will likely never
come back.
The "hot" languages right now are Java, VB, and C#. Plenty of job ads for
these skills -- much more than for Perl.
But this type of big-project work will soon be done outside the United States,
so these "hot" languages will be as cold as Python or Ruby.
Nor is offshoring limited to programming. It started with programming because
programmers commanded such high wages, especially in the late 1990's. But
make no mistake about it, administration can be offshored much more easily
than programming, and so can tech support.
The days of big money and great benefits working with computers at large corporations
are over, no matter what you do or what language you use.
The Good News
Big business isn't the only employer. In good times, it isn't even the main
employer. Startups employ many technologists, and startups are less equipped
to deal with the large outsourcers capable of handling an offshore coding
crew. Startups are likely to employ one or two well rounded, hotshot computer
guys. Computer guys who are equally at home coding up an app as they are upgrading
a server operating system. Guys like you.
This recession will end. I think if the Democrats take the white house in
2005, and I think that's a real possibility, the recession will end very soon.
If the current crew maintains control, it might take longer. But this recession
will end -- the joblessness will end -- hiring will resume. And when it does,
small companies will be hiring well rounded hotshots like you.
When you get into one of these small companies, you'll be their sole source
of tech knowledge. If you whip out a needed app in a day, they'll sing your
praises, whether or not you use corporationally correct technology. In such
an environment, Perl is your friend. As stated several times in this magazine,
Perl lets you prototype an app in days, and then perfect it over time. Perl
is a glue language that can bring together distinct apps from different vendors.
Perl can be used to expose legacy apps through web interfaces.
To the extent that you're a ninja in your chosen language, you'll be able
to work miracles and make good money. That's why it's vital to get lots of
practice in a very productive language. Given the job market and what Perl
can do, for me Perl is that language.
The Perl Mercenary Goes Anywhere
Like all good mercenaries, the Perl mercenary chooses sides based on money.
He or she might work Perl for Linux one day, and Perl for Windows the next.
Most Linux and UNIX machines come loaded with Perl. Windows machines normally
don't come loaded with Perl, but it's easy enough to get. ActiveState (URL
in URL's section) is the Cadillac of the industry, providing Perl and Python
for Windows, as well as a host of other tools facilitating high productivity
programming. You can also obtain the Siemens Perl distribution for Windows
(URL in URL's section).
My experience has been that, when adjusted for directory separators and path
separators, and not using OS dependencies, Perl programs written on Linux
run identically on Windows, and vice versa.
Where to Find Perl Information
By Steve Litt
Perl's so ubiquitous that there's no shortage of Perl information.
General Purpose Perl Books
I like two general purpose Perl books, Core Perl and Perl 5 for Dummies. I
occasionally also use Programming Perl.
Perl 5 for Dummies
You have a job interview in 2 days, and one of the skills they ask for is
Perl. Your Perl isn't too good. What do you do?
Read Perl 5 for Dummies, ISBN 0-7645-0044-9, by Paul Hoffman. It defines enough
Perl terms that you'll sound authoritative at your interview.
This book is organized easy tasks to hard tasks, so it's an excellent learning
tool. It covers all the bases: text, math, arrays, hashes, branching
and looping, CGI, file IO, regex, and OOP. This book takes you beyond beginner
skills into the intermediate range. It might be called "For Dummies", but
if you master this material you'll be a competant Perl programmer.
Hoffman's immaculate organization makes this book an outstanding reference
volume. You can find almost anything in the book by glancing at the table
of contents. There are many Perl books out there -- most more advanced than
Perl 5 for Dummies, but Hoffman's book provides such lightning fast lookup
that I carry it everywhere I go.
Perl 5 for Dummies is great for Perl syntax. With nothing but that book, you
can write substantial Perl programs. But Perl is a "many ways to perform a
task" type of language, and some of those ways are easier than others. To
become a truly productive programmer, you must read a more advanced book...
Core Perl
Core Perl, ISBN 0-13-035181-4, by Reuven M. Lerner, starts where Perl 5 for
Dummies left off. Like the For Dummies book, its organization is crystal clear,
intuitive, and obvious. But Core Perl is much more advanced.
Many advanced books read like man pages. No examples -- really bright people
don't need examples. What -- you can't read BNF notation? Then you shouldn't
program!
Core Perl is not one of those elitist books. Examples abound. Everything's
understandable, even without a computer science PH.D or an IQ over 200. It's
plain English and plain code.
Yet everything's there. If it comes with the basic Perl distribution, it's
covered. Completely.
Perl 5 for Dummies is organized easiest to hardest, because it's primarily
a learning tool. Core Perl starts with syntax, then subroutines, strings and
regex, modules, objects, tying, file IO, networking, relational databases,
database apps, maintenance and security, CGI and web apps, mod_perl, and mason.
The book is over 500 pages, and by page 140 you've completed all Perl syntax,
features and subroutines. The majority of the book focuses on the tough stuff.
Armed with this book, you can walk into almost any Perl situation and write
the right app.
Programming Perl
I view Programming Perl, ISBN 1-56592-149-6 as a backup for Core Perl. If
I can't find it in Core Perl, I'll find it in Programming Perl.
Programming Perl is short on organization, long on authority, and written
by industry leaders. This book is written by Perl inventor Larry Wall,
Tom Christianson and Randall Schwartz -- a who's who of Perl society. The
equivalent Linux book would be written by Linus Torvalds and Alan Cox. If
you need a piece of information, it's in here. If you need to know the best
way to do something, it's in here. The only problem is it will be hard to
find.
This book is best kept as a reference backup for Core Perl, and as rainy day
reading so that one day you'll be a true Perl master.
Specialized Perl Books
Mastering Perl/Tk
None of us likes programming GUI. We'd rather concern ourselves with the problem
domain. But sometimes we need to program GUI. With Perl, the easiest way
to go GUI is by writing HTML. But HTTP is stateless, and sometimes that makes
things too difficult.
One of the best ways to GUIize Perl is with the Tk widget set. Mastering Perl/Tk,
ISBN 1-56592-716-8, by Steve Lidie and Nancy Walsh, is the perfect book to
fulfil its title -- mastering Perl/Tk. All the information, and I mean ALL
the information, is in here. The organization is perfect, both for learning
and for lookup. Its 23 granular chapters are named to make it obvious where
to look for what you need. The organization is crystal clear, and the contained
information is everything you need. If you need to GUIize Perl, you need
this book.
Web References
Steve Litt's Perls of Wisdom
When you're coding up an app under a crushing deadline and minute scrutiny,
this is the website you want for Perl information. Billed as a website giving
you "The 10% you need -- for 90% of your work", it gives you
the syntax you need to quickly code loops, branches, subroutines, references,
OOP, regex, CGI, arrays, hashes, and more. Unlike man pages and packaged Perl
documentation, examples are plentiful. The organization is clean and optimized
for quick lookup.
The regex page for Litt's Perls of Wisdom has a worldwide reputation for being
one of the best ever explanations of regular expressions, and it's read by
Perl programmers and non-Perl programmers alike. The page on references and
subroutines walks you through all the syntax do's and don'ts, with examples.
If you program Perl, this web page is indispensable.
CPAN
Perl's a great language, but what makes it truly great is the huge number
of premanufactured modules available for specific tasks. All these modules
are gathered at the CPAN (Comprehensive Perl Archive Network) website. There
are exquisite lookup facilities, both by category drilldown and by word search.
Most of the modules have excellent documentation so you can see what they
do, and how to do it, before downloading and installing them.
If you're faced with a programming task, and you don't immediately know how
to do it, pay a visit to CPAN. It's likely someone has already done it, and
your solution is just a download away.
Rapid Learning Perl
By Steve Litt
20 years ago you could spend 2 years learning a language, and still have
plenty of time to profit from it. Now that time is more like 2 weeks, or,
if you have an upcoming interview where one of the requirements is Perl,
2 days. How do you do it? Here's how:
- Hello World
- Scalar Variables
- String and numeric manipulation
- Looping
- If/elsif/else
- Arrays
- Regular Expressions
- File I/O
- Hashes
- References
- Subroutines
- Objects
When Rapid Learning Perl, use Steve Litt's Perls of Wisdom, and optionally,
use the excellent book Perl 5 for Dummies.
Hello World
Create the following file, called test.pl:
Now run that file with the following command:
perl ./test.pl
The file will run and produce the following output:
[slitt@mydesk slitt]$ perl ./test.pl Hello World [slitt@mydesk slitt]$
|
The next step is to make the file itself executable, so you needn't manually
invoke perl on the command line. To do that, add a "shebang" line
before the print statement:
#!/usr/bin/perl -w print "Hello World\n";
|
The line in red is called a "shebang" line, because it starts with a comment
character (#), then an exclamation point, which is often called
a "bang", and then the command through which to feed the remainder of the
program. That program is perl, which resides in /usr/binon
most systems. The -w tells the Perl interpreter to issue warnings.
Perl without warnings is iffy at best -- the program appears to run smoothly
but bugs produce erroneous output -- often subtle errors..
Next, set the program executable with the following command:
chmod a+x ./test.pl
Finally, run the program with the following command:
./test.pl
The program's output is identical -- all you've done is eliminated the need
for manually invoking perl on the command line.
Finally, you need to make Perl a little stricter than normal. Normal, default
Perl is a loosy-goosey language allowing the programmer to do almost any
wild and crazy thing. Perl gladly accepts the most bizarre programming requests,
and do what it thinks is best with them. It's the wild wild west.
You don't want that. You want a language with rules, and an interpreter that
will stop and make you correct it if those rules are violated. To do that,
insert the use strict syntax, as shown in the following code:
#!/usr/bin/perl -w use strict; print "Hello World\n";
|
Always insert the use strict line right after the shebang line.
Doing so elimates a lot of hard to find bugs.
Congratulations
Congratulations! You've created a Perl program, run it from the Perl interpreter,
run it from the command line, and set it to reject illegal syntax. Next stop,
scalar variables...
Scalar Variables
Perl has three types of variables -- scalars, arrays (lists) and hashes.
A scalar variable is a variable that isn't an array or a hash. Scalar variables
always begin with a dollar sign. Scalar variables can hold one of three types
of values:
- Numeric value
- String value
- Reference
We'll postpone references to later in this tutorial.
Modify your test.pl so it looks like this:
#!/usr/bin/perl -w use strict; my $var = "Hello World\n"; print $var;
|
The output is exactly like the output in the Hello World article, because
your only change is that you put the string into a scalar variable, called
$var, before printing it.
Notice the word my preceding the variable. The word my
declares$var to be local to the subroutine or block within which
it resides. In this case that block is the entire program, but usually it
will be in a block delineated by braces { and }. Always
use my. First, global variables are the kiss of death in any programming
language, including Perl. Second, the use strict declaration will
flag as an error variables declared without the my, unless special
procedures are followed.
Next, add the following 2 lines:
#!/usr/bin/perl -w use strict; my $var = "Hello World\n"; print $var; $var = 32; print "Number is ", $var * 10, "\n";
|
In the preceding code you reassigned a number to $var, and used it as a number
in the next print statement. In C you must declare a variable to be either
a number or a character pointer, or one of many other distinct types. Perl
allows you to change the type of a variable simply by assigning a value of
a different type to it.
This is both good and bad. It's good because you needn't go through the convoluted
logic C requires just to perform an obvious operation on different types
of variables. It's bad because C's strict typechecking is one more way to
prevent errors and bugs. On the whole I'd say that for most modern programming,
C's typechecking is counterproductive, and Perl's non-typechecking is an
advantage, as long as you're careful.
The preceding program produces the following output:
[slitt@mydesk slitt]$ ./test.pl Hello World Number is 320 [slitt@mydesk slitt]$
|
As you can see, $var was treated as a numeric, so it could be multiplied
by 10 in the print statement. Note also that the print statement can print
a string and a number, without any type conversion or cast, simply by putting
commas between the elements to be printed. Simpler than printf or
cout, isn't it.
Congratulations
Congratulations! You've used scalar variables to contain both numbers and
strings, printed the results, and even multipled a numeric variable by 10.
Let's discuss a little more about manipulation of variables...
Numeric and String Manipulation
Numeric manipulation is arithmetic. I won't give you Perl's order of precidence
-- I don't know it myself. I use parentheses liberally, ensuring the intended
calculation. Generally speaking, if you understand C or Java arithmetic,
you understand Perl's:
Operator
|
Functionality
|
Example
|
$var contains
|
=
|
Assignment
|
$var = 8;
|
8
|
+
|
Addition
|
$var = 8 + 3;
|
11
|
-
|
Subtraction
|
$var = 8 - 3;
|
5
|
*
|
Multiplication
|
$var = 8 * 3;
|
24
|
/
|
Fractional Division
(5/2 is 2.50, not 2)
|
$var = 8 / 3;
|
2.66666666666667
|
%
|
Modulus
|
$var = 8 % 3
|
2
|
|
Whole number division
|
$var = (8-(8%3))/3
|
2
|
Perl's numeric relational operators are similar to C's:
Operator
|
Functionality
|
Example
|
>
|
Numeric greater than
|
if($var > 8)
|
>=
|
Numeric greater than or equal
|
if($var >= 8) |
<
|
Numeric less than
|
if($var < 8) |
<=
|
Numeric less than or equal |
if($var <= 8) |
==
|
Numeric equality
|
if($var == 8) |
String Manipulation
Use the dot operator to concatinate 2 strings:
$var = "George" . " " . "W." . " " . "Bush";
In the preceding, $var now contains "George W. Bush".
You often need the length of a string. Use the length() function
to get that.
The opposite of concatination is truncation. Personally, I do most truncation
with regular expressions, which are discussed later in this tutorial. However,
you can also use the substr(). substr() is faster than
regular expressions, so if you're truncating in a tight loop, you might want
to usesubstr(). Its syntax is as follows:
substr EXPR,OFFSET,LENGTH,REPLACEMENT
LENGTH and REPLACEMENT are optional. Personally, I seldom
use REPLACEMENT because it's clearer to replace text with a concatenation
after truncation. Here are some ways you can use substr() to truncate
from the right and from the left of a string. In the following examples,
imagine that:
$string = "Perl Developer":
Task
|
Statement
|
Value of $var
|
Remove 3 characters from the front of the string
|
$var = substr($string, 3);
|
"l Programmer"
|
Remove all but the 3 final characters from the string
|
$var = substr($string", -3); |
"mer"
|
Remove the 3 final characters from the string |
$var = substr($string, 0, - 3);
|
"Perl Program"
|
Remove all but the first 3 chaaracters from the string
|
$var = substr($string, 0, 3);
|
"Per"
|
To remove specific text, you can use the index()and rindex()
functions to find the offset of the start of a substring, and then plug that
number into the OFFSET or LENGTH arguments of substr(). Information
on length(), substr(), index() and rindex()are
contained in their respective man pages.
Perl's string relational operators are distinct from its numeric relational
operators:
Operator
|
Functionality
|
Example
|
gt
|
String greater than
|
if($var gt "cccc")
|
ge
|
String greater than or equal
|
if($var ge "cccc") |
lt
|
String less than
|
if($var lt "cccc") |
le
|
String less than or equal |
if($var le "cccc") |
eq
|
String equality
|
if($var eq "cccc") |
Congratulations
Congratulations! You've learned how to manipulate strings, perform arithmetic,
and compare both numbers and strings. Now it's time to create loops.
Looping
Computers are dumb -- all they can do is perform repeated calculations very
fast. That repetition involves loops. Here's the simplest loop:
#!/usr/bin/perl -w use strict; for(my $ss = 1; $ss <= 10; $ss++) { print "$ss\n"; }
|
The preceding program prints the numbers 1 through 10. This for
loop is very similar to what you'd find in C. Notice the word my
within the parentheses of the for loop. That makes $ss
local to the for loop. That's good, unless you want to use the number
outside the loop, in which case you'd declare $ss above the loop.
Another Perl looping mechanism is the while loop. The following program
prints 1 through 10 in exactly the same format:
#!/usr/bin/perl -w use strict; my $ss = 1; while($ss <= 10) { print "$ss\n"; $ss++; }
|
In the preceding, the variable is incremented within the loop's block, instead
of within the loop itself. Note also that $ss is declared above the
loop, so it can be used below the loop.
Loops can be terminated with the last statement. The following code
adds a last statement, and also uses the final value of the loop
in a print statement below the loop:
#!/usr/bin/perl -w use strict; my $ss = 1; while($ss <= 10) { print "$ss\n"; if($ss >= 3) { last; } $ss++; } print "Final value is ", $ss, ".\n";
|
Obviously, in the preceding code there's no need for the ($ss <= 10)
because the block of the loop terminates at 3. However, often times the conditional
within the while statement and the conditional triggering the last
statement are not related, so that which of the two terminates the loop will
be known only at runtime. One example would be a loop reading a file that
terminates either on a line containing the word "terminate" or at end of
file.
Finally, a special loop construct reads each element of an array and acts
on it. Here's an example:
#!/usr/bin/perl -w use strict; my @praises = ("fast", "stable", "easy"); foreach my $praise (@praises) { print "Perl is ", $praise, "\n"; }
|
In the preceding, @praises is an array (a list in Perlspeak)
initiialized to have elements "fast", "stable" and "easy". You might wonder
why no parentheses are required around my $praise (@praises) when
parentheses are required with the for and while constructs.
Welcome to the world of Perl -- like a human language, it has exceptions.
When run, the preceding program prints out the following:
[slitt@mydesk slitt]$ ./test.pl Perl is fast Perl is stable Perl is easy [slitt@mydesk slitt]$
|
Congratulations
Congratulations! You've now used three types of loops, and learned how to
terminate a loop either by exceeding the conditional, or by encountering alast
statement within the block of the loop. Next stop, if/elsif/else.
If/elsif/else
Every computer language has facilities by which you can do different things
depending on different conditions. In Perl, you do it with if/elsif/else
conditionals. Here's the basis:
#!/usr/bin/perl -w use strict; for(my $number = 1; $number <= 6; $number++) { print $number, " :"; if($number > 5) { print "The number is greater than 5\n"; } elsif($number > 3) { print "The number is greater than 3 but less than 6\n"; } elsif($number == 3) { print "The number is equal to 3\n"; } else { print "The number is less than 3\n"; } }
|
The preceding program loops through 1 through 6, and for each, prints the
number, and then prints whether it's greater than 5, between 5 and 3, equal
to 3, or less than 3, based on the if/elsif/else statement. The following
is the output of the program:
x
[slitt@mydesk slitt]$ ./test.pl 1 :The number is less than 3 2 :The number is less than 3 3 :The number is equal to 3 4 :The number is greater than 3 but less than 6 5 :The number is greater than 3 but less than 6 6 :The number is greater than 5 [slitt@mydesk slitt]$
|
Be careful -- order counts. The first condition to be true causes the program
to skip over the rest of the elsif and else statements.
Thus, if we had tested for greater than 3 before testing for greater than
5, everything bigger than 3 would have printed as bigger than 3 but less than
6 -- clearly an error. Note also that you can have as many or as few elsif
clauses as you need. Sometimes you need only if and else,
and sometimes you don't even need the else. There is never more
than one else.
In order to reduce double negatives and the like, you can use the unless
clause, which looks something like this:
unless($number > 1000)
{
print "Number less than 1000\n";
}
Another branching method is short circuit logic. For instance:
writeLogFile() || writeErrorFile() || die "cannot write files";
The preceding calls writeLogFile(). If that returns non-zero, nothing
to the right of it is called. But if it returns 0, then writeErrorFile()is
called. If that returns non zero, nothing to the right is called. However,
if it returns zero, then the program dies with the message "cannot write files".
When used right this can be a readable and compact way of programming.
Less Common Constructs
In keeping with Perl's "many ways to do it", there are other branching constructs.
You might not want to code this way if you're not a "many ways to do it"
type of guy. BUT, any Perl programmer must be familiar enough with these
constructs that he or she knows what they do when he or she encounters them
in somebody else's code.
last if x == 0;
print "Not interested\n" unless $price < 1500;
The preceding are if and unless statements respectively,
but they're written with the action first and the test second. The main advantage
is that parentheses and braces are not required, making this a much more readable
form for if and unless statements with a single line of
code for the action. Do not use these constructs if there are several statements
to be executed, as it would be terribly confusing to someone reading your
code.
Then there's the <=> operator. This operator, which operates
on 2 numeric arguments, returns -1 if the number to its left is smaller than
the one to its right, 0 if they're equal, and 1 if the one to the left is
greater than the one to its right. Check out this program:
#!/usr/bin/perl -w use strict; for(my $number = 1; $number <= 6; $number++) { print $number, " :"; print "<=>", $number <=> 3, "\n"; }
|
In the precding each number was checked against 3 to see if it was smaller,
equal, or bigger, and the result was printed on the line. Thus this single
operator is a tri-state detector. The <=> operator is used
extensively as a callback routine to custom sorts.
[slitt@mydesk slitt]$ ./test.pl 1 :<=>-1 2 :<=>-1 3 :<=>0 4 :<=>1 5 :<=>1 6 :<=>1 [slitt@mydesk slitt]$
|
There is a text equivalent of the <=> operator, the cmp
operator.
Congratulations
Congratulations! You've learned how to use if/elsif/else constructs
to implement conditional execution. You've also learned to implement conditional
execution with unless statements, short circuit logic, action-first
if and unless statements, and the <=> tri-state
operator. Now it's time for arrays.
Arrays
Most computer programs use arrays. Arrays are used to hold many items, identified
by number (that number being the subscript). In Perl, arrays are called "lists".
Unlike most other computer languages, a single Perl array can hold elements
consisting of very different types. However, from a data processing standpoint
there's seldom a reason to do that -- arrays usually represent several like
entities.
Array variables, when referring to an entire array, begin with an at sign
(@). Thus, @books would be an array, but $books is a scalar. But,
$books[2] refers to the third element of the @books array. Sound confusing?
You bet it is! Here's a summary:
@books
|
An array variable (list variable)
|
$books[3]
|
The fourth element of the @books array
|
$books
|
A scalar not related to @books
|
You can declare an array like this:
my @books;
You can declare and initialize an array like this:
my @books = ("Cujo", "Carrie", "Christine");
Look carefully at the preceding. Contrary to every instinct of an experienced
computer programmer, the elements are inside parentheses, not square brackets.
As if that's not confusing enough, when directly initializing a reference
to an array, square brackets are used for that purpose. References are discussed
later in this article.
The easiest way to iterate through an array is to use the foreach
construct. Here's a program that does that:
#!/usr/bin/perl -w use strict; my @books = ("Cujo", "Carrie", "Christine"); foreach my $book (@books) { print $book, "\n"; }
|
In the preceding, note that $book is a scalar that, for each iteration,
is assigned the next element of @books.
You can also do it in a more C-like way:
#!/usr/bin/perl -w use strict; my @books = ("Cujo", "Carrie", "Christine"); my $subscript = 0; print "Subscript of last element is ", $#books, "\n"; while($subscript <= $#books) { print $subscript, ": ", $books[$subscript], "\n"; $subscript++; }
|
In the preceding note that $#books is a scalar that takes the value
of the subscript of the last element in the @books array.
NOTE
Do not confuse length() with $#arrayname. The former returns
the length of a string, and is meaningless when applied to
an array. The latter returns the subscript of the last element of an array,
and is meaningless when applied to a string.
Unlike C strings, Perl strings are not an array of characters, so there's
no relationship between arrays and strings. There are, however, functions
to convert between strings and arrays.
|
join and split convert between strings and arrays
Let's say you want to convert between strings and arrays. You can use the
join() function to convert an array to a string, and the split()
function to convert a string to an array. See the following:
#!/usr/bin/perl -w use strict; my @books = ("Cujo", "Carrie", "Christine"); my $saying = "To be or not to be, that is the question"; my $bookstring = join(", ", @books); print $bookstring, "\n"; my @sayingarray = split(/ /, $saying); foreach my $element (@sayingarray) { print $element, "\n"; }
|
The preceding program converts array @books into scalar string $bookstring,
and converts string $saying into array @sayingstring, and
then prints them both. The following is the output of the program:
[slitt@mydesk slitt]$ ./test.pl Cujo, Carrie, Christine To be or not to be, that is the question [slitt@mydesk slitt]$
|
Accessing Array Elements
The simplest way to access an element of an array is through its subscript.
For instance:
#!/usr/bin/perl -w use strict; my @books = ("Cujo", "Carrie", "Christine"); print $books[1], "\n"
|
Element 1, which is the second element, is "Carrie", as shown in the following
output:
[slitt@mydesk slitt]$ ./test.pl Carrie [slitt@mydesk slitt]$
|
To add or change an element, just assign to it:
#!/usr/bin/perl -w use strict; my @books = ("Cujo", "Carrie", "Christine"); $books[10] = "Firestarter"; $books[2] = "The Stand"; my $ss = 0; foreach my $book (@books) { print $ss, ": ", $book, "\n" if defined($book); $ss++; } print "The last element has subscript ", $#books, "\n";
|
Note that we assigned a value to $book[10], leaving elements 3 through
9 undefined. The output is interesting:
[slitt@mydesk slitt]$ ./test.pl 0: Cujo 1: Carrie 2: The Stand 10: Firestarter The last element has subscript 10 [slitt@mydesk slitt]$
|
Stacks and FIFOs
Four functions, push(), pop(), shift() and unshift()
enable you to easily implement various stacks, fifo's, and other constructs.
Here are some brief descriptions:
Function
|
Action graphic
|
Description
|
push @DESTARRAY, @SRCARRAY
|
0
|
1
|
2
|
3
|
4
|
MEM
v
|
*
|
*
|
*
|
*
|
*
|
v
|
|
Places the elements of the @SRCARRAY at
@DESTARRAY's end ( starting immediately after the current
final used subscript)
|
pop @ARRAY
|
0
|
1
|
2
|
3
|
4
|
MEM
|
*
|
*
|
*
|
*
|
*
|
^
_/
|
|
Returns and removes the array's highest subscripted
element.
|
shift @ARRAY
|
MEM
|
0
|
1
|
2
|
3
|
4
|
^
\_
|
<-
|
<-
|
<-
|
<-
|
<-
|
|
Returns and removes the array's element 0, and then
shifts all other elements down one.
|
unshift @DESTARRAY, @SRCARRAY
|
MEM
|
0
|
1
|
2
|
3
|
4
|
`->
|
->
|
->
|
->
|
->
|
->
|
|
Shifts all elements in @DESTARRAY up (higher
subscript) by the number of elements in @SRCARRAY, and then copies
the elements of @SRCARRAY to the (now) unused lower elements of
@DESTARRAY.
|
push() and pop() implement a stack, while push()
and shift() implement a FIFO (First in first out). The shift()
function can grab function arguments off the function argument array, one
member at a time, starting with arg 0 ($_[0]).
Splice
The splice()function is beyond the scope of this tutorial, but it
can replace sections of an array. If you need this functionality, look up
splice() in the perlfunc man page.
Congratulations
Congratulations! You have now created arrays, looped through them with foreach,
joined them into a string with join(), created them from a string
with split(), accessed array elements by subscript, and manipulated
arrays with functions, push(), pop(), shift()
and unshift().
File I/O
There are several ways to access files. You can use the oldstyle capital letter
barewords, a scalar variable, a filehandle using the FileHandle module, or
a file glob. For simplicity and versitility, this article discusses only the
scalar variable method.
File Output
The following program writes an array to a file:
#!/usr/bin/perl -w use strict; my $outputfile; open($outputfile, ">outfile.txt") || die "Could not open for output\n"; my @books = ("Cujo", "Carrie", "Christine"); foreach my $book (@books) { print $outputfile $book, "\n"; } close($outputfile);
|
Notice that there is NOT a comma between the file variable and the string
to print. This is vitally important, as you'll get an unfathomable error if
you try to place a comma there. By using a scalar as the file variable, you
can pass the file into and out of functions and subroutines to your heart's
content.
File Input
There are two styles of file input:
- Reading a line at a time in a loop
- Immediately reading the entire file into an array
Assume that you have a file called infile.txt whose three lines are
"Cujo", "Carrie", and "Christine". The following program reads each line of
the file and prints it, much like any other programming language:
#!/usr/bin/perl -w use strict; my $inputfile; open($inputfile, "<infile.txt") || die "Could not open for input\n"; while(<$inputfile>) { my $line = $_; chomp($line); print $line, "\n"; } close($inputfile);
|
Notice the chomp() function. That function removes the trailing newline
from the line, so that you can process the line as a string.
Compare the preceding to the following program, which opens the file, quickly
stuffs it into an array, and then uses that array to print the lines:
#!/usr/bin/perl -w use strict; my $inputfile; open($inputfile, "<infile.txt") || die "Could not open for input\n"; my @lines = <$inputfile>; close($inputfile); foreach my $line (@lines) { chomp($line); print $line, "\n"; }
|
The line at a time method has the advantage of less memory usage. Using a
line at a time, you could efficiently read a gigabyte file. Trying to place
the contents of the gigabyte file into an array would bring most computers
to their knees.
The whole file technique has the advantage of being more of a snapshot. Notice
that you can close the file immediately after access, rather than waiting
for the process to complete. This is helpful if the process is time consuming,
and other programs are waiting to access the file. Also, with the file in
an array, you can reprocess it without rereading it, and you can access lines
at random.
Congratulations
Congratulations! You've written and read files. If you need to use the other
methods, get a good Perl book and use them.
Hashes
In Perl, a hash is a bunch of key->value pairs. Lookup is by key.
A hash can easily represent an entity, or a single database row (record).
For instance:
#!/usr/bin/perl -w use strict; my %person = ( 'fname'=>'John', 'lname'=>'Smith', 'job'=>'Perl Developer' ); print "First name: ", $person{'fname'}, "\n"; print "Last name: ", $person{'lname'}, "\n"; print "Job: ", $person{'job'}, "\n";
|
The preceding code outputs the following:
[slitt@mydesk slitt]$ ./test.pl First name: John Last name: Smith Job: Perl Developer [slitt@mydesk slitt]$
|
Notice the following about the preceding program and output:
- "Field" lookup is by name. Typically, this is exactly what you want.
- "Fields" are defined by strings at runtime. Contrast this with C, where
you must create structs and typedefs. It's often handy to be able to add a
"field" on the fly.
- The "fields" are not stored in any particular order, unlike in C.
There might be occasions when you do not know the keys. This could happen,
for instance, if you parse an XML file into a hash. In such cases, you could
use the keys() function to derive all the keys, and then iterate
through the keys:
#!/usr/bin/perl -w use strict; my %person = ( 'fname'=>'Zev', 'lname'=>'Smith', 'job'=>'Perl Developer' ); my @keys = keys %person; foreach my $key (@keys) { print $key, ": ", $person{$key}, "\n"; }
|
The preceding code outputs the following:
[slitt@mydesk slitt]$ ./test.pl lname: Smith fname: Zev job: Perl Developer [slitt@mydesk slitt]$
|
Notice the fields are neither in the order entered, nor in alpha order. They
are not ordered. A hash is not ordered. If you want it ordered, you need to
sort it by keys or by values. To sort it by keys, use the sort()
function:
my @keys = sort keys %person;
To sort it in reverse order, use the reverse() function or
my @keys = reverse sort keys %person;
Another way to get the reverse sort is with the block syntax of sort():
my @keys = sort {$b cmp $a} keys %person;
Speaking of the block syntax of sort, it's a necessity if you want to sort
by values instead of keys:
my @keys = sort {$person{$a} cmp $person{$b}} keys %person;
In the preceding line, $a represents element ss in the array, while
$b represents element ss+1. Everything in the block is considered
the sort's comparison subroutine, be called back for each pair of array elements.
In this case, instead of comparing the elements, we compare the hash values
associated with the elements. Pretty cool, huh?
Adding and Deleting Hash Elements
In the following program, we start with a hash whose keys are alpha representations
'one', 'two' and 'three', with corresponding values 1, 2 and 3. We then add
an element 'four'=>4, and use the delete() function to delete the element
whose key is 'two'. The result prints out 1, 3 and 4 -- just what you'd expect:
#!/usr/bin/perl -w use strict; my %numbers = ( 'one'=>1, 'two'=>2, 'three'=>3 );
$numbers{'four'} = 4; delete($numbers{'two'});
foreach my $key (sort {$numbers{$a} <=> $numbers{$b}} keys %numbers) { print $key, ": ", $numbers{$key}, "\n"; }
|
[slitt@mydesk slitt]$ ./test.pl one: 1 three: 3 four: 4 [slitt@mydesk slitt]$
|
Detecting Existence of Hash Elements
To see whether a key exists, use the following syntax:
if(exists($hashname{$keystring}) {do_something();}
To see whether an existing (or nonexistent) key's value is defined, use this:
if(defined($hashname{$keystring}) {do_something();}
Constructing Complex Data With Hashes
Arrays hold scalars. References are scalars. That means that you could have
an array of references to hashes. If all the referenced hashes have the same
keys (column names in DBMS speak), then the array of references to hashes
is exactly like a database table minus any indexes. You could create an index
for a column by creating a hash whose key is the column value for each record
and whose value is the array subscript.
Additionally, a hash element's value can be a reference, meaning you could
have a hash of hash references, or a hash of array references. In practice,
there's no end to the data constructs you can create with references, scalars,
arrays and hashes.
Congratulations
Congratulations! You've created a hash, added and deleted elements, accessed
elements by name, accessed all elements with the keys() function,
sorted and reverse sorted by key and by value, and learned about the construction
of complex data.
References
References are the coolest thing since sliced bread. They're like pointers
in C, but without the danger. A reference is simply a scalar that refers
to another variable -- typically an array or a hash. This is important because
it gives the means for multidimensional arrays and hierarchical hashes. It
also yields much more flexibility in passing information into and out
of subroutines and objects. The following chart shows how to reference and
dereference different variable types, and how to instantiate them and how
to instantiate references to them.
Variable |
Instantiating
the variable
|
Instantiating a
reference to it
|
Referencing it |
Dereferencing it |
Accessing an element |
$scalar |
$scalar = "steve";
|
$ref = \"steve";
|
$ref = \$scalar |
$$ref or
${$ref} |
N/A |
@list |
@list = ("steve", "fred");
|
$ref = ["steve", "fred"];
|
$ref = \@list |
@{$ref} |
${$ref}[3]
$ref->[3]
|
%hash |
%hash = ("name" => "steve",
"job" => "Troubleshooter");
|
$hash = {"name" => "steve",
"job" => "Troubleshooter"}; |
$ref = \%hash |
%{$ref} |
${$ref}{"president"}
$ref->{"president"}
|
FILE |
|
|
$ref = \*FILE |
{$ref} or scalar <$ref> |
|
In the preceding, note the rich variety of dereferencing techniques. You
can place the proper symbol in front of the brace enclosed reference variable.
In the case of references to scalars you can simply place an additional dollar
sign. Perhaps most useful of all, you can dereference an element of an array
reference or a hash reference with the -> operator. This makes
coding much easier, as you can operate directly on the reference rather than
dereferencing it first -- a real time saver when you have hash elements that
are themselves references to other hashes containing references. The following
code shows just how handy it can be:
#!/usr/bin/perl -w use strict;
package Me;
sub new { my($type) = $_[0]; my($self) = {}; $self->{'name'} = 'Bill Brown';
### Make a reference to an empty array of jobs $self->{'jobs'} = [];
### Now make each element of array referenced by ### $self->{'jobs'} a REFERENCE to a hash! $self->{'jobs'}->[0]={'ystart'=>'1998','yend'=>'1999','desc'=>'Bus driver'}; $self->{'jobs'}->[1]={'ystart'=>'1999','yend'=>'1999','desc'=>'Bus mechanic'}; $self->{'jobs'}->[2]={'ystart'=>'1999','yend'=>'2001','desc'=>'Software Developer'};
bless($self, $type); return($self); }
### showResume is coded to show off the -> operator. In real ### life you'd probably use a foreach loop, but the following ### while(1) loop better demonstrates nested -> operators. sub showResume { my($self)=$_[0]; print "Resume of " . $self->{'name'} . "\n\n"; print "Start\tEnd\tDescription\n"; my $ss = 0;
# Loop through array referenced by $self->{'jobs'}, # and for each subscript, print the value corresponding # to the hash key. In other words, print every field of # every record of the jobs array while (1) { last unless defined $self->{'jobs'}->[$ss]; print "$self->{'jobs'}->[$ss]->{'ystart'}\t"; print "$self->{'jobs'}->[$ss]->{'yend'}\t"; print "$self->{'jobs'}->[$ss]->{'desc'}\n"; $ss++; } }
package Main;
my $me = Me->new(); $me->showResume(); print "\nFirst job was $me->{'jobs'}->[0]->{'desc'}.\n";
|
In the preceding, the Me type object has an element called jobs
that is a reference to an array of jobs. Each element in that array is itself
a reference to a hash whose elements contain various pieces of information
like start date, end date and description. Look how neatly the showResume()method
is written. Now imagine the while loop in that method without benefit of
the-> operator. Not a pretty picture.
I usually pass references to arrays and hashes into subroutines, and pass
references to them out. After all, how often do you pass an array or hash
you don't want modified?
Subroutines
Subroutines are how we split big tasks into small ones. It's how we get work
done in an easy to understand manner. You declare a subroutine like this:
sub printHello() { ### code ### return value if any
|
The preceding had no arguments. Here's how you declare a subroutine with
arguments:
sub biggest($$) { $a = shift; ### arg0 $b = shift; ### arg1 ### code ### return value if any }
|
Here's a subroutine that returns the biggest of two numbers:
sub biggest($$) { $a = shift; $b = shift; if($a >= $b) { return $a; } else { return $b; } }
|
Calling the subroutine is easy:
my $wwii = 1941; my $gulfWar = 1990; my $bigger = biggest($wwii, $gulfwar); ### $bigger now contains 1990.
|
Often a subroutine's arguments are references to a hash or array. Typically
you'll operate on them directly with the -> operator. If, however,
for some reason you want to operate on a copy, leaving the original alone,
you can dereference the reference, creating a copy:
Calling the subroutine |
Constructing the subroutine |
%globals; # ... # set globals # ...
# now print globals &printGlobals(\%globals);
|
sub printGlobals { # copy of argument precludes extra-scope change my(%globals) = %{$_[0]}; print "Current Dir: $globals{'currentdir'}\n"; print "Program Dir: $globals{'programdir'}\n"; print "Version : $globals{'programver'}\n"; print "Accesslevel: $globals{'accesslevel'}\n"; }
|
Objects
That leaves us with objects. Perl didn't support objects natively, so when
Perl became OOP they were incorporated as a sort of reference to hash. Here's
an example:
#!/usr/bin/perl -w use strict;
package Person;
sub new($$) { my($type) = shift; #arg0 my($self) = {}; $self->{'name'} = shift; #arg1 bless($self, $type); return($self); }
sub setAge($$) { my $self = shift; #arg0 $self->{'age'} = $_[1]; }
sub getAge($) { my $self = shift; #arg0 return $self->{'age'}; }
sub getName($) { my $self = shift; #arg0 return $self->{'name'}; }
package Main; use Person;
my $me = Person->new('Steve Litt'); $me->setAge(16); ### Yeah, right print $me->getName(), " is ", $me->getAge, " years old\n";
|
Let's analyze the preceding. The Person class's definition starts
with the package Person statement, and ends with either the next
package statement or with the end of the file. The class (object,
whatever) is entirely defined by subroutines. Data elements of the class
are created in the subroutine body by setting elements of the $selfhash
reference (it's a reference to a hash). What turns the whole thing into an
object or class or whatever you want to call it is the bless statement,
which relates the $self hash reference to the type declared in the
main routine (Person->new('SteveLitt')).
When you use the $me->setAge(16) syntax, it's the same as using
$me as the first argument to Person::setAge(). In other
words, it's equivalent to Person::setAge($me,16). That's always
important to remember, especially when objects get tricky, like when you
use callback routines.
Summary
That's it. You've learned the basics of Scalar Variables, Numeric and String
Manipulation, Looping, If/elsif/else, Arrays, File I/O, Hashes, References,
Subroutines, and Objects. Armed with this information and a book, you can
write a fairly substantial Perl program. Go get em, tiger!
Life After Windows: Free Software RAD
Life After Windows is a regular Linux Productivity Magazine column,
by Steve Litt, bringing you observations and tips subsequent to Troubleshooters.Com's
Windows to Linux conversion.
By Steve Litt
RAD. It stands for Rapid Application Development. Most RAD is promises and
snake oil, but it does exist in this world. Since the early 1990's, you can
buy the Clarion application development environment, and create a 5 table
app in a day. In a week you can work miracles. I've never seen such a productive
computer language, before or since. Not Python, not Perl, not Delphi, not
Powerbuilder, nothing.
But Clarion isn't available on Linux.
For non-database apps, Python and Perl come fairly close. With their regular
expressions, object orientation and loose typing, you can bang out an app
fast. But not as fast as you might want. So I've been looking for faster
coding methodologies using Free Software on Linux.
One huge success is my Node.pm file. It's basically a knockoff of
DOM (Document Object Model) specially adapted to outlines, but capable in
any hierarchical situation. You instantiate a parser object to parse an outline
into a node tree, then you code two callback routines -- one to execute on
first entry to a node, and the other (optional) to execute on final exit
from the node. Then you instantiate a Walker object, with the top node on
the tree and the callback routines as arguments, and that Walker object does
all the work. You can have several successive walker objects using several
different callback routines to accomplish almost any transformation on the
hierarchy.
Several years ago I constructed an EMDL to UMENU parser. EMDL stands for
Easy Menu Definition Language, and it's implemented as an outline. That makes
menu authoring fast for the touch typist. But UMENU is not hierarchical --
each menu has its own config file, so that UMENU systems are constructed
level by level, not recursively. Think of the hassles of converting a recursive
outline to a level oriented series of files. I created the EMDL to UMENU
parser, but it was almost unmaintainable, ununderstandable, not able to be
modifed to output other menu types, and it took about 15 seconds to process
a 200 item menu system.
Then I made a new EMDL to UMENU parser using Node.pm. It took a couple days,
and the logic is crystal clear. Implementing outputs for Icewm menus or web
menus or almost any other type of menu is fairly easy. The new parser runs
in 2 seconds instead of 15. On any job requiring manipulation of hierarchies,
Node.pm can boost productivity by a factor of 10.
Node.pm isn't the only RAD technique on the block.
Noel Henson, the maintainer of the VimOutliner project, uses the /rdb
4gl. /rdb is basically a series of UNIX filters that manipulate data. Data
is piped through selects and joins and edit screens. The beauty is that each
of these small executables is ultimately modular, with their input defined
by stdin and their output going to stdout. At either of those places you
can "pick off" the data for troubleshooting. Noel says using /rdb he can
whip out a 5 table app in a day.
In an Infoworld article called "Test before you leap into development", author
Jon Udell describes a methodology where the tests are created first,
and serve as a definition for the desired application. One of the things
Udell stressed was testing of individual modules, which implies a need for
modularity. I thought of Noel Henson's statement that building a /rdb app
was like putting together Legos (R), and decided to try coding
an app like that. My first attempt was very simple and got done in a day.
My second was tougher and more thought provoking...
I have a program to keep track of the state of various peoples' domain names
via whois information. Unfortunately, whois information is not given out in
data format, but instead in a rather freeform text that varies with top level
domain and registrar. My old domain tracking program worked well enough when
everyone used network solutions and had only .com domains, but as choices
in registrars and top level domains increased, more parsers were needed.
I decided to rewrite the domain tracking program using the piped small executable
philosophy. I had it somewhat working in a day, and almost completely working
in 2. Here is the design:

In the preceding, all but the cat program were written in Perl. The splitter
took the file full of whois records, split them into single records, and using
Perl's pipes (open($pipe,'|'.$command)) piped each data set out
to the piped pair of the registrar, match and toplevel parser, and the parser
router. The parser router, based on the toplevel domain and the registrar,
assembled the proper pipeline of parsers to parse the data, then piped to
that pipeline via a Perl pipe.
This design and coding method enabled me to assemble and test a unit at a
time, with a high degree of confidence in each component. Components were
tested repeatedly with proper input files until confidence was achieved. The
last step was to use my UMENU program to assemble these components into a
user friendly app, so that the user would not need to construct pipelined
commands on the command line. The UMENU assembly took a remarkable 15 minutes.
Armed with the UMENU app as documentation, I then added a web interface (by
adding a single executable that converts straight text to a web page and then
constructing a web page with links too call the various pipelines).
So in about 2 days I had a complete app that correctly parsed .net, .com and
.biz from two different registrars. There was only one problem...
This app took 19 seconds to run, as opposed to the 1.5 seconds its predecessor
took. I don't mind paying for quick development in runtime efficiency, but
a tenfold speed decrease is a little excessive. Some bottleneck analysis revealed
that the problem was executable startup and data passage throughout the system
-- there was no one bottleneck so there was no magic bullet. The only way
to speed it up was to condense several executables into one. Specifically,
I placed the registrar, match and toplevel parser, as well as the parser router,
within the domain splitter. This was pretty much a matter of copying the
code into the .pm file that housed system wide subroutines, and then calling
those from the domain splitter. Additionally, I had to debug through constant
regression tests.
Second, I condensed the registrar and registrant parsers into one. This eliminated
some reusable code because one registrar parser worked for both registrars.
Once again, debugging and regression testing got the app performing exactly
as the completely modular one had. The new design ran in 9.5 seconds and
looked something like this:

Piped executables is definitely a route to a high quality 1 or 2 day app,
and serves as a prototype for faster apps, as well as a baseline for regression
testing when developing the higher performance version. Whether runtime speed
and scalability issues can be adequately addressed by this design and coding
methodology is something I'll need to research more.
Steve Litt is the author of the course
on the Universal Troubleshooting Process. He can be reached at his email address.
Letters to the Editor
All letters become the property of the publisher (Steve Litt), and may
be edited for clarity or brevity. We especially welcome additions, clarifications,
corrections or flames from vendors whose products have been reviewed in this
magazine. We reserve the right to not publish letters we deem in bad taste
(bad language, obscenity, hate, lewd, violence, etc.).
Submit letters to the editor to Steve Litt's email address, and be sure
the subject reads "Letter to the Editor". We regret that we cannot return
your letter, so please make a copy of it for future reference.
How to Submit an Article
We anticipate two to five articles per issue, with issues coming out monthly.
We look for articles that pertain to the Linux or Open Source. This can be
done as an essay, with humor, with a case study, or some other literary device.
A Troubleshooting poem would be nice. Submissions may mention a specific product,
but must be useful without the purchase of that product. Content must greatly
overpower advertising. Submissions should be between 250 and 2000 words long.
Any article submitted to Linux Productivity Magazine must be licensed with
the Open Publication License, which you can view at http://opencontent.org/openpub/.
At your option you may elect the option to prohibit substantive modifications.
However, in order to publish your article in Linux Productivity Magazine,
you must decline the option to prohibit commercial use, because Linux Productivity
Magazine is a commercial publication.
Obviously, you must be the copyright holder and must be legally able to
so license the article. We do not currently pay for articles.
Troubleshooters.Com reserves the right to edit any submission for clarity
or brevity, within the scope of the Open Publication License. If you elect
to prohibit substantive modifications, we may elect to place editors notes
outside of your material, or reject the submission, or send it back for modification.
Any published article will include a two sentence description of the author,
a hypertext link to his or her email, and a phone number if desired. Upon
request, we will include a hypertext link, at the end of the magazine issue,
to the author's website, providing that website meets the Troubleshooters.Com
criteria for links and that the author's
website first links to Troubleshooters.Com. Authors: please understand we
can't place hyperlinks inside articles. If we did, only the first article
would be read, and we can't place every article first.
Submissions should be emailed to Steve Litt's email address, with subject
line Article Submission. The first paragraph of your message should read as
follows (unless other arrangements are previously made in writing):
Copyright (c) 2003 by <your name>. This material
may be distributed only subject to the terms and conditions set forth in the
Open Publication License, version Draft v1.0, 8 June 1999 (Available
at http://www.troubleshooters.com/openpub04.txt/ (wordwrapped for readability
at http://www.troubleshooters.com/openpub04_wrapped.txt). The latest version
is presently available at http://www.opencontent.org/openpub/).
Open Publication License Option A [ is | is not] elected,
so this document [may | may not] be modified. Option B is not elected, so
this material may be published for commercial purposes.
After that paragraph, write the title, text of the article, and a two sentence
description of the author.
Why not Draft v1.0, 8 June 1999 OR LATER
The Open Publication License recommends using the word "or later" to describe
the version of the license. That is unacceptable for Troubleshooting Professional
Magazine because we do not know the provisions of that newer version, so it
makes no sense to commit to it. We all hope later versions will be better,
but there's always a chance that leadership will change. We cannot take the
chance that the disclaimer of warranty will be dropped in a later version.
Trademarks
All trademarks are the property of their respective owners. Troubleshooters.Com(R)
is a registered trademark of Steve Litt.
URLs Mentioned in this Issue
- MISC URLs
- Free Software/Open Source URL's
- Perl Distributions
- Perl Websites
- Other Development Environments
- http://www.softvelocity.com:
Home of Clarion -- the quickest software development methodology I've ever
used. Hey guys, port it to Linux!
- http://www.rdb.com:
Home of the /rdb database software, based on piping together small executables.
I've heard you can bang out a 5 table app in a day using /rdb.
- http://www.python.org: Home of
Python, the most readable language on the planet.
- http://www.ruby-lang.org/en/:
Home of Ruby, the Open Source interpreter built from the ground up to be
OOP.
_