Sort
i
ng
41
descending numerical order, you would simply have to reverse the order of the
comparison of 
$a
and 
$b
like so:
sub desc_numerically {
return $b <=> $a;
}
Another way of handling this is to sort the data in ascending order and reverse the
list using Perl’s built-in 
reverse
function like this:
@out = reverse sort numerically @in;
There is also another operator, 
cmp
, which returns the same values but orders the
elements lexically. The original default sort is therefore equivalent to:
@out = sort lexically @in;
sub lexically {
return $a cmp $b;
}
3.1.2 Complex sor
t
s
Sorts as simple as the ones we’ve discussed so far are generally not written using the
subroutine syntax that we have used above. In these cases, the block syntax is used.
In the block syntax, Perl code is placed between the 
sort
function and the input
list. This code must still work on 
$a
and 
$b
and must obey the same rules about
what it returns. The sorts that we have discussed above can therefore be rewritten
like this:
@out = sort { $a <=> $b } @in;
@out = sort { $b <=> $a } @in; # or @out = reverse sort { $a <=> $b } @in
@out = sort { $a cmp $b } @in;
The subroutine syntax can, however, be used to produce quite complex sort crite-
ria. Imagine that you have an array of hashes where each hash has two keys, fore-
name and surname, and you want to sort the list like a telephone book (i.e.,
surname first and then forename). You could write code like this:
my @out = sort namesort @in;
sub namesort {
return $a->{surname} cmp $b->{surname}
|| $a->{forename} cmp $b->{forename};
}
Note that we make good use of the “short circuit” functionality of the Perl 
||
oper-
ator. Only if the surnames are the same and the first comparison returns 
0
is the sec-
ond comparison evaluated.
Add pdf to word file - software Library dll:C# PDF Convert to Word SDK: Convert PDF to Word library in C#.net, ASP.NET, WinForms, WPF application
Online C# Tutorial for Converting PDF to Word (.docx) Document
www.rasteredge.com
Add pdf to word file - software Library dll:VB.NET PDF Convert to Word SDK: Convert PDF to Word library in vb.net, ASP.NET, WinForms, WPF application
How to Convert PDF to Word (.docx) Document in VB.NET
www.rasteredge.com
42
CHAPTER 
U
s
eful Perl 
i
d
i
om
s
We can, of course, mix numeric comparisons with lexical comparisons and even
reverse the order on some comparisons. If our hash also contains a key for age, the
following code will resolve two identical names by putting the older person first.
my @out = sort namesort @in;
sub namesort {
return $a->{surname} cmp $b->{surname}
|| $a->{forename} cmp $b->{forename}
|| $b->{age} <=> $a->{age};
}
This default sort mechanism is implemented using a Quicksort algorithm. In this
type of sort, each element of the list is compared with at least one other element in
order to determine the correct sequence. This is an efficient method if each com-
parison is relatively cheap; however, there are circumstances where you are sorting
on a value which is calculated from the element. In these situations, recalculating
the value each time can have a detrimental effect on performance. There are a
number of methods available to minimize this effect and we will now discuss some
of the best ones.
3.1.3 The Orcish Manoeuvre
One simple way to minimize the effect of calculating the sort value multiple times is
to cache the results of each calculation so that we only have to carry out each calcu-
lation once. This is the basis of the Orci
s
h Manoeuvre (a pun on “or cache”) devised
by Joseph Hall. In this method, the results of previous calculations are stored in a
hash. The basic code would look like this:
my %key_cache;
my @out = sort orcish @in;
sub orcish {
return ($key_cache{$a} ||= get_sort_key($a))
<=> ($key_cache{$b} ||= get_sort_key($b));
}
sub get_sort_key {
# Code that takes the list element and returns
# the part that you want to sort on
}
There is a lot going on here so it’s worth looking at it in some detail.
The hash 
%key_cache
is used to store the precalculated sort keys.
The function 
orcish
carries out the sort, but for each element, before calculat-
ing the sort key, it checks to see if the key has already been calculated, in which case
software Library dll:VB.NET PDF Password Library: add, remove, edit PDF file password
This VB.NET example shows how to add PDF file password with access permission setting. passwordSetting.IsAssemble = True ' Add password to PDF file.
www.rasteredge.com
software Library dll:C# PDF Password Library: add, remove, edit PDF file password in C#
This example shows how to add PDF file password with access permission setting. passwordSetting.IsAssemble = true; // Add password to PDF file.
www.rasteredge.com
Sort
i
ng
43
it will be stored in 
%key_cache
. It makes use of Perl’s 
||=
operator to make the
code more streamlined. The code 
$key_cache{$a} ||= get_sort_key($a)
can be expanded to
$key_cache{$a} = $key_cache{$a} || get_sort_key($a)
The net effect of this code is that if 
$key_cache{$a}
doesn’t already exist then
get_sort_key
is called to calculate it and the result is stored in 
$key_cache{$a}
.
The same procedure is carried out for 
$b
and the two results are then compared
using 
<=>
(this could just as easily be 
cmp
if you need a lexical comparison).
Depending on how expensive your 
get_sort_key
function is, this method can
greatly increase your performance in sorting large lists.
3.1.4 Schwar
t
zian 
t
ransform
Another way of avoiding recalculating the sort keys a number of times is to use the
Schwartzian transform. This was named after Randal L. Schwartz, a well-known mem-
ber of the Perl community and author of a number of Perl books, who was the first per-
son to post a message using this technique to the comp.lang.perl.misc newsgroup.
In the Schwartzian transform we precalculate all of the sort keys before we begin
the actual sort.
As an example, let’s go back to our list of 
CD
s. If you remember, we finally
decided that we would read the data file into an array of hashes, where each hash
contained the details of each 
CD
. Figure 3.1 is a slightly simplified diagram of the
@CD
s array (each hash has only two fields). 
Suppose that now we want to produce a list of 
CD
s arranged in order of release
date. The naïve way to write this using 
sort
would be like this:
my @CDs_sorted_by_year = sort { $a->{year} <=> $b->{year} } @CDs;
We could then iterate across the sorted array and print out whatever fields of the
hash were of interest to us.
0
h
a
s
h
r
e
f
h
a
s
h
r
e
f
1
y
e
a
r
1972
titl
e
Ziggy St
a
rdu
s
t
y
e
a
r
1971
titl
e
Hunky Dory
Figur
e
3.1
Th
e
un
s
ort
e
d array of CD ha
s
h
e
s
software Library dll:VB.NET Create PDF from Word Library to convert docx, doc to PDF in
Support Windows 32bit and 64bit system. VB.NET Demo Code for Converting Word to PDF. Add necessary references: RasterEdge.Imaging.Basic.dll.
www.rasteredge.com
software Library dll:C# PDF File & Page Process Library SDK for C#.net, ASP.NET, MVC
PowerPoint; C#: Create PDF from Tiff; C#: Convert PDF to Word; C#: Convert C# Read: PDF Image Extract; C# Write: Insert text into PDF; C# Write: Add Image to PDF
www.rasteredge.com
44
CHAPTER 
U
s
eful Perl 
i
d
i
om
s
As you can see, to get to the sort key (the release date) we have to go through a
hash reference to get to that hash itself. Hash lookup is a reasonably expensive oper-
ation in Perl and we’d be better off if we could avoid having to look up each ele-
ment a number of times.
Let’s introduce an intermediate array. Each element of the array will be a refer-
ence to a two-element array. The first element will be the year and the second ele-
ment will be a reference to the original hash. We can create this list very easily
using 
map
.
my @CD_and_year = map { [$_->{year}, $_] } @CDs;
Figure 3.2 shows what this new array would look like.
The year field in each hash has been extracted only once, which will save us a lot of
time. We can now sort our new array on the first element of the array. Array lookup is
much faster than hash lookup. The code to carry out this sort looks like this:
my @sorted_CD_and_year = sort { $a->[0] <=> $b->[0] } @CD_and_year;
Figure 3.3 shows this new array.
0
a
r
r
a
y
r
e
f
a
r
r
a
y
r
e
f
1
0 1971
1
h
a
s
h
r
e
f
y
e
a
r
1972
titl
e
Ziggy St
a
rdu
s
t
y
e
a
r
1971
titl
e
Hunky Dory
0 1972
1
h
a
s
h
r
e
f
Figur
e
3.2 @CD_and_year contain
s
r
e
f
e
r
e
nc
e
s
to a two 
e
l
e
m
e
nt array
0
a
r
r
a
y
r
e
f
a
r
r
a
y
r
e
f
1
0 1972
1
h
a
s
h
r
e
f
y
e
a
r
1971
titl
e
Hunky Dory
y
e
a
r
1972
titl
e
Ziggy St
a
rdu
s
t
0 1971
1
h
a
s
h
r
e
f
Figur
e
3.3 @sorted_CD_and_year i
s
@CD_and_year 
s
ort
e
d by th
e
fir
s
e
l
e
m
e
nt of th
e
array
software Library dll:C# Create PDF from Word Library to convert docx, doc to PDF in C#.
C#.NET Sample Code: Convert Word to PDF in C#.NET Project. Add necessary references: RasterEdge.XDoc.PDF.dll. using RasterEdge.XDoc.Word;
www.rasteredge.com
software Library dll:C# PDF insert image Library: insert images into PDF in C#.net, ASP
using RasterEdge.Imaging.Basic; using RasterEdge.XDoc.PDF; Have a try with this sample C#.NET code to add an image to the first page of PDF file.
www.rasteredge.com
Sort
i
ng
45
Now in 
@sorted_CD_and_year
we have an array of references to arrays. The
important thing, however, is that the array is ordered by year. In fact, we only need
the second element of each of these arrays, because that is a reference to our origi-
nal hash. Using 
map
it is simple to strip out the parts that we need.
my @CDs_sorted_by_year = map { $_->[1] } @sorted_CD_and_year;
Figure 3.4 shows what this array would look like.
Let’s put those three stages together.
my @CD_and_year = map { [$_, $_->{year}] } @CDs;
my @sorted_CD_and_year = sort { $a->[1] <=> $b->[1] } @CD_and_year;
my @CDs_sorted_by_year = map { $_->[0] } @sorted_CD_and_year;
That, in a nutshell, is the Schwartzian transform—a sort surrounded by two 
map
s.
There is one more piece of tidying up that we can do. As each of the 
map
s and the
sort
take an array as input and return an array we can chain all of these transforma-
tions together in one statement and lose both of the intermediate arrays.
my @CDs_sorted_by_year = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [$_, $_->{year}] } @CDs;
If this doesn’t look quite like what we had before, try tracing it through in reverse.
Our original array (
@CD
s) is passed in at the bottom. It goes through the 
map
that
dereferences the hash, then the 
sort
, and finally the last 
map
.
The chaining together of multiple list processing functions, where the output of
the first map becomes the input to the sort and so on, is very similar to the 
I
/
O
pipes that we saw when looking at the 
UNIX
filter model earlier.
The Schwartzian transform can be used anywhere that you want to sort a list of
data structures by one of the data values contained within it, but that’s not all it can
do. Here’s a three-line script that prints out our 
CD
file (read in through 
STDIN
),
sorted by the recording label.
print map { $_->[0] }
sort { $a->[1] cmp $b->[1] }
map { [$_, (split /\t/)[2]] } <STDIN>;
0
h
a
s
h
r
e
f
h
a
s
h
r
e
f
1
y
e
a
r
1971
titl
e
Hunky Dory
y
e
a
r
1972
titl
e
Ziggy St
a
rdu
s
t
Figur
e
3.4
@CDs_sorted_by_year contain
s
ju
s
t th
e
ha
s
h r
e
f
e
r
e
nc
e
s
from 
@sorted_CD_and_year
software Library dll:VB.NET PDF insert image library: insert images into PDF in vb.net
using RasterEdge.XDoc.PDF; Have a try with this sample VB.NET code to add an image to the first page of PDF file. ' Open a document.
www.rasteredge.com
software Library dll:VB.NET PDF File Compress Library: Compress reduce PDF size in vb.
Also able to uncompress PDF file in VB.NET programs. Offer flexible and royalty-free developing library license for VB.NET programmers to compress PDF file.
www.rasteredge.com
46
CHAPTER 
U
s
eful Perl 
i
d
i
om
s
3.1.5 The Gu
t
t
man
-
Rosler 
t
ransform
At the 1999 Perl Conference, Uri Guttman and Larry Rosler presented a paper on
sorting with Perl. It covered all of the techniques discussed so far and went a step
further, by introducing the concept of the packed-default sort. They started from
two premises:
1
Eliminating any hash or array dereferencing would speed up the sort.
2
The default lexical sort (without any sort subroutine or block) is the fastest.
The resulting method is an interesting variation on the Schwartzian transform.
Instead of transforming each element of the list into a two element list (the sort
key and the original data) and sorting on the first element of this list, Guttman and
Rosler suggest converting each element of the original list into a string with the
sort key at the beginning and the original element at the end. A list containing
these strings can then be sorted lexically and the original data is extracted from the
sorted list.
The example that they use in their paper is that of sorting 
IP
addresses. First they
convert each element to a string in which each part of the 
IP
address is converted
(using 
pack
) to the character represented by that value in 
ASCII
. This four-character
string has the original data appended to the end:
my @strings = map { pack('C4', /(\d+)\.(\d+)\.(\d+)\.(\d+)/) . $_ } @IPs;
then the strings are lexically sorted using the default 
sort
mechanism
my @sorted_strings = sort @strings
and finally the original data is extracted.
my @sorted @IPs = map { substr($_, 4) } @sorted_strings;
Rewriting this to make it look more like the Schwartzian transform, we get this:
my @sorted_IPs = map { substr($_, 4) }
sort
map { pack('C4', /(\d+)\.(\d+)\.(\d+)\.(\d+)/) . $_ } @IPs;
This type of sort needs a bit more thought than the other methods that we have
considered in order to create a suitable string for sorting; however, it can pay great
dividends in the amount of performance improvement that you can see.
3.1.6 Choosing a sor
t
t
echnique
If you are having performance problems with a program that contains a complex
sort, then it is quite possible that using one of the techniques from this section will
speed up the script. It is, however, possible that your script could get slower. Each of
Databa
s
e Interface 
(
DBI
)
47
the techniques will improve the actual sort time, but they all have an overhead which
means that your sort will need to be quite large before you see any improvement. 
When selecting a sort technique to use, it is important that you use the bench-
marking methods, discussed in section 3.4, to work out which technique is most
appropriate. Of course, if your script is only going to run once, then spending half a
day benchmarking sorts for the purpose of shaving five seconds off the runtime isn’t
much of a gain. 
This section has only really started to discuss the subject of sorting in Perl. If
you’d like to know more, Guttman and Rosler’s paper is a very good place to start.
You can find it online at http:
/
/
www.hpl.hp.com
/
personal
/
Larry_Rosler
/
sort
/
.
3.2
Da
t
abase In
t
erface (DBI)
As discussed in chapter 1, a common source or sink for data is a database. For
many years Perl has had mechanisms that enable it to talk to various database sys-
tems. For example, if you wanted to exchange data with an Oracle database you
would use 
oraperl
and if you had to communicate with a Sybase database you
would use 
sybperl
. Modules were also available to talk to many other popular
database systems.
Most of these database access modules were a thin Perl wrapper around the pro-
gramming 
API
s that were already provided by the database vendors. The mecha-
nisms for talking to the various databases were all doing largely the same thing, but
they were doing it in completely incompatible ways.
This has all changed in recent years with the introduction of the generic Perl
Database Interface (
DBI
) module. This module was designed and written by Tim
Bunce (the author and maintainer of 
oraperl
). It allows a program to connect to
any of the supported database systems and read and write data using exactly the same
syntax. The only change required to connect to a different database system is to
change one string that is passed to the 
DBI
connect function. It does this by using
different database driver (
DBD
) modules. These are all named 
DBD::<db_name>
.
You will need to obtain the 
DBD
module for whichever database you are using sepa-
rately from the main 
DBI
module. 
3.2.1 Sample 
DBI
program
A sample 
DBI
program to read data from a database would look like this:
1: #!/usr/local/bin/perl –w
2:
3: use strict;
4: use DBI;
5:
48
CHAPTER 
U
s
eful Perl 
i
d
i
om
s
6: my $user = 'dave';
7: my $pass = 'secret';
8: my $dbh = DBI->connect('dbi:mysql:testdb', $user, $pass,
9:
{RaiseError => 1})
10:
|| die "Connect failed: $DBI::errstr";
11:
12: my $sth = $dbh->prepare('select col1, col2, col3 from my_table')
13:
14: $sth->execute;
15:
16: my @row;
17: while (@row = $sth->fetchrow_array) {
18:
print join("\t", @row), "\n";
19: }
20:
21: $sth->finish;
22: $dbh->disconnect;
While this is a very simple 
DBI
program, it demonstrates a number of important
DBI
concepts and it is worth examining line by line.
Line 1 points to the Perl interpreter. Notice the use of the 
-w
flag.
Line 3 switches on the 
strict
pragma.
Line 4 brings in the 
DBI.pm
module. This allows us to use the 
DBI
functions.
Lines 6 and 7 define a username and password that we will use to connect to the
database. Obviously, in a real program you probably wouldn’t want to have a pass-
word written in a script in plain text.
Line 8 connects us to the database. In this case we are connecting to a database
running 
M
y
SQL
. This free database program is very popular for web systems. This is
the only line that would need to change if we were connecting to a different data-
base system. The 
connect
function takes a number of parameters which can vary
depending on the database to which you are connecting. The first parameter is a
connection string. This changes its precise meaning for different databases, but it is
always a colon-separated string. The first part is the string 
dbi
and the second part is
always the name of the database system2 that we are connecting to. In this case the
string 
mysql
tells 
DBI
that we will be talking to a 
M
y
SQL
database, and it should
therefore load the 
DBD::mysql
module. The third section of the connection string
in this case is the particular database that we want to connect to. Many database sys-
tems (including 
M
y
SQL
) can store many different databases on the same database
server. In this case we want to connect to a database called testdb. The second and
third parameters are valid usernames and passwords for connecting to this database.
2
Or, more accurately, the name of the DBD module that we are using to connect to the database.
Data
:
:
Dumper
49
The fourth parameter to 
DBI->connect
is a reference to a hash containing various
configuration options. In this example we switch on the 
RaiseError
option, which
will automatically generate a fatal run-time error if a database error occurs.
The 
DBI->connect
function returns a database handle, which can then be used
to access other 
DBI
functions. If there is an error, the function returns 
undef
. In
the sample program we check for this and, if there is a problem, the program dies
after printing the value of the variable 
$DBI::errstr
which contains the most
recent database error message.
Line 12 prepares an 
SQL
statement for execution against the database. It does
this by calling the 
DBI
function 
prepare
. This function returns a statement handle
which can be used to access another set of 
DBI
functions—those that deal with exe-
cuting queries on the database and reading and writing data. This handle is unde-
fined if there is an error preparing the statement.
Line 14 executes the statement and dies if there is an error.
Line 16 defines an array variable which will hold each row of data returned from
the database in turn.
Lines 17 to 19 define a loop which receives each row from the database query and
prints it out. On line 17 we call 
fetchrow_array
which returns a list containing
one element for each of the columns in the next row of the result set. When the
result set has all been returned, the next call to 
fetchrow_array
will return the
value 
undef
Line 18 prints out the current row with a tab character between each element.
Lines 21 and 22 call functions that reclaim the memory used for the database
and statement handles. This memory will be reclaimed automatically when the vari-
ables go out of scope, but it is tidier to clean up yourself.
This has been a very quick overview of using the 
DBI
. There are a number of
other functions and the most useful ones are listed in appendix A. More detailed
documentation comes with the 
DBI
module and your chosen 
DBD
modules.
3.3
Da
t
a
:
:
Dumper
As your data structures get more and more complex it will become more and more
useful to have an easy way to see what they look like. A very convenient way to do
this is by using the 
Data::Dumper
module which comes as a standard part of the
Perl distribution. 
Data::Dumper
takes one or more variables and produces a
“stringified” version of the data contained in the variables. 
We’ll see many examples of 
Data::Dumper
throughout the book but, as an
example, let’s use it to get a dump of the 
CD
data structure that we built in the pre-
vious chapter. The data structure was built up using code like this:
50
CHAPTER 
U
s
eful Perl 
i
d
i
om
s
my @CDs;
my @attrs = qw(artist title label year);
while (<STDIN>) {
chomp;
my %rec;
@rec{@attrs} = split /\t/;
push @CDs, \%rec;
}
In order to use 
Data::Dumper
we just need to add a 
use
Data::Dumper
statement
and a call to the 
Dumper
function like this:
use Data::Dumper;
my @CDs;
my @attrs = qw(artist title label year);
while (<STDIN>) {
chomp;
my %rec;
@rec{@attrs} = split /\t/;
push @CDs, \%rec;
}
print Dumper(\@CDs);
Running this program using our 
CD
files as input produces the following output:
$VAR1 = [
{
'artist' => 'Bragg, Billy',
'title' => 'Workers\' Playtime',
'year' => '1987',
'label' => 'Cooking Vinyl'
},
{
'artist' => 'Bragg, Billy',
'title' => 'Mermaid Avenue',
'year' => '1998',
'label' => 'EMI'
},
{
'artist' => 'Black, Mary',
'title' => 'The Holy Ground',
'year' => '1993',
'label' => 'Grapevine'
},
{
'artist' => 'Black, Mary',
'title' => 'Circus',
'year' => '1996',
'label' => 'Grapevine'
},
Documents you may be interested
Documents you may be interested