Professional Documents
Culture Documents
/usr/bin/perl
###########################################################################
################ Professor Liang's Perl Tutorial In Perl #################
# You should run the code in this tutorial with "perl perltutorial.txt"
# so you can see what exactly each code fragment is doing. Before each
# example I print a number in the form "1: ", "2: ", etc..., so you can
# correlate the output with the source code. You should also experiment
# by making changes to the code.
# In any other language, you would think me crazy: how can you multiply a
# string by an integer? Surely this is a mistake only a beginner will make.
# However, in Perl, you will in fact see 6! How? Because strings are the
# basic data type in Perl. Most everything is treated as strings, including
# the unquoted 3. Perl is best in practice for text processing, such as
# with the html source of web pages. It is actually quite awful for
# manipulating binary data (C would be best for that purpose). Strings
# therefore have a special status in Perl.
# Perhaps one reason for the popularity of Perl is all those dollar signs!
# Most variables (except for file handles) in Perl require a special symbol
# in front to designate its type. The most common symbol is "$".
# The $ symbol in Perl signifies the presence of a scalar value. Scalar
# values include numerical values, strings, and perhaps most importantly,
# pointers (memory addrs). All variables containing scalar values must be
# prefixed with $. This is a characteristic that Perl inherited from Unix
# scripting languages, though it has transcended that simple role long ago.
# If you are familiar with Java, scalars correspond to fixed, primitive
# types (plus strings).
# Like C (and unlike Java), Perl has no special type for booleans. The
# null pointer, 0, "", or even "0" all represent false. Everything else
# represents true. You'll sometimes see Perl code such as
# if (2<1)
# if (1<2) cout << "me";
# else cout << "no, me";
# It won't print anything, but you'll have to know that the "else" is by
# convention associated with the closest (innermost) "if", otherwise you
# may get confused. This is the classic "dangling else" problem. The
# required {}'s of Perl help to eliminate this confusion.
# Why are these things called arrays and not just lists? Because Perl
# gives the user the convenient syntax of accessing list elements using
# the familiar bracket notation:
# NOT FAIR! If l is an array/list then why did we still put $ in front of it?
# This is a point of contention in the Perl community, and may change in
# the future. The reason for the $ is that, although l is an array, l[2]
# is an integer, which is a scalar. That's just the way things are now
# with Perl. If the value of l[2] is also an array, we would use @l[2].
# The expression $#l is the last valid index of l, or the length of l minus
# one. Note that $i is local (by virtue of "my") within the loop. An
# alternative is just to say my $i = @l-1; Perl will automatically infer
# from the given context of assigning an array to a scalar that by @l you
# really mean the length of the list.
## Hash tables are also very basic data structures in Perl. Here's a table
# one might use to store those Hofstra student id's:
# The % symbol prefixes the hash table, while the {}'s (as opposed to []'s)
# signify that you're accessing a hash table instead of an ordinary array.
# The function "keys" returns a list containing all the keys of a hash table:
# the foreach loop goes through every element of a list, and inside the
# body of the loop $_ refers to the current value of the list being examined.
sub fib1
{ my $n = $_[0];
if ($n<2) {1} else {fib1($n-1) + fib1($n-2)}
}
# Several things are important to point out. The parameters of the subroutine
# "fib1" are contained in the implicit array "_". Thus $_[0] is the
# first argument, and $_[1] would be the second, and so on. Secondly,
# the "return" keyword is optional in perl: whatever is the last
# expression evaluated determines the value returned by the function.
# You might be wondering: why did I have to declare a local variable $n?
# Can't I just use $_[0] throughout? Well, for this function it doesn't
# matter, but Perl passes variables to a function in a different way than
# what you might expect:
sub swap
{ my $temp;
$temp = $_[0];
$_[0] = $_[1];
$_[1] = $temp # The semicolon is optional on the last line in {}'s
}
$x = 2; $y = 3;
swap($x,$y);
print "\n11: the values of \$x and \$y are now $x and $y: they got swapped!\n";
my ($x,$y);
($x,$y) = ($y,$x);
# The naive fibonacci function will give you the same answer, but
# you'll have to wait around 20,000 years to see it.
# Just to be complete, here's "fib3", which uses a while loop (happy now?)
sub fib3
{ my $n = shift; # alternative to my $n = $_[0];
my ($a,$b) = (1,1); # initial values for $a and $b
while ($n>1)
{ ($a,$b) = ($b,$a+$b); $n--; }
$b # the ; is optional for the last line inside {}'s
}
my $x = 3;
my $z = \$x; # sets $z to point to $x. "\" works like "&" in C.
$$z += 1; # to buy back the value from the pointer, you need two dollars :-)
print "14: the value that $z points to is ", $$z, "\n";
# To illustrate when pointers are needed, let's first look at a function that
# does NOT require them. The following function returns the index of an
# element $x inside a list @L, returning -1 if it doesn' exist:
sub indexof
{
my ($x,@L) = @_; # returns position of x inside L
my $i = 0;
while (($i <= $#L) && ($x != $L[$i])) {$i++;}
if ($i<=$#L) {$i} else {-1} # return -1 if $x not found in list.
}
# indexof(3,(4,3,6,8,7)) will return 1, the index of the "3" inside the list.
# This function did not need pointers because Perl nicely separates the
# head (or "car") of the list from the rest ("cdr") of the list in the way
# you'd expect. However, sometimes you may want to pass in something else
# AFTER the list, or pass two distinct lists to a function. The next
# function returns the intersection of two lists. Note the use of pointers,
# and deduce for yourself why they're needed.
sub intersection
{
my ($A,$B) = @_; # assigns two POINTERS to the args
my @I = (); # intersection list to be constructed, initially null
foreach my $x (@$A) # for each element x in A,
{
foreach my $y (@$B) # check if it's also in B
{
if ($x == $y) { @I=($x,@I) } # add x to I list (can also use push)
} # inner loop
} # outer loop
@I; # return the I list that was built.
}
my @l = (1,3,4,7,2,8);
my @m = (3,9,6,4,1);
print "16: the intersection of @l and @m is ", intersection(\@l, \@m), "\n";
# Look at the code carefully to see where pointers are making a difference:
# For example, @$B retrieves the list from the pointer $B. Also,
# $$B[$i] is required to access the (scalar) values of the list.
# In order to pass a complex structure to a function, in general you'll
# have to use pointers. In the above function, at least the first list
# had to be passed in as a pointer.
$myhash->{"key1"} = "value1";
$myhash->{"key2"} = "value2";
# Perl infers from {} and -> that $myhash is a pointer to a hash table.
# C/C++ programmers should know that "A->B" is really "(*A).B". That is,
# it dereferences the pointer A and at the same time retrieves the field
# B from the dereferenced struct/object. Perl expands this meaning of "->"
# to the case of arrays, hash tables (and as you will see in the next
# section, even functions). For arrays you can similarly have
$A->[0] = 1;
$A->[1] = 2;
print "17: referenced array: ", @$A, "\n"; # prints contents of array
# Just as in C/C++, one way to avoid confusion with pointers is to use them
# in a CONSISTENT manner In fact, this observation led to the uniform
# treatment of pointers in the Java language. That is, if you adopt the
# policy to:
# Note the ";" at the end, since this is just an assignment statement!
# No name follows "sub" - it's just "lambda" for Perl. To apply the
# function pointed to by $fib, we use $fib->(args). So now you see:
# Having said that however, I should point out one subtlety: the
# definition of $fib wouldn't have worked if I had used my $fib = ...;
# because the recursive calls to $fib would refer to something not defined
# yet. "my" in Perl corresponds to "let" in functional languages
# such as Scheme. However, Scheme contains another construct "letrec" that
# allows one to bind recursive definitions. Perl lacks this construct, but
# fortunately it's not a big deal. To bind $fib to a local var, simply
# declare it first on a separate line:
# my $fib;
# $fib = sub { ... };
$tfib = sub {
my $f; # local recursive function
$f = sub
{ my ($n,$a,$b) = @_;
if ($n<2) {$b} else {$f->($n-1,$b,$a+$b)}
};
$f->($_[0], 1, 1); # call internal function
};
# Now to call $tfib, we can just say $tfib->(10), without having to pass in
# the two 1's.
sub mapfun
{
my ($f,@L) = @_; # separate car,cdr of @_ into function and list
my @M =(); # new list to be built
foreach my $x (@L) { push( @M, $f->($x) ); }
@M # return new list
}
@powers = mapfun($f,(1,2,3,4,5,6,7,8,9,10));
print "18: this is how computer people count: ", join(" ",@powers), "\n";
# It's also possible to inline a function when passing it, without defining
# it first:
sub compose
{
my ($f,$g) = @_; # parameters are functions $f and $g
sub { $f->($g->(@_)); } # fog(x) = f(g(x))
}
# note that:
@l = mapfun($f,@l);
# will effectively replace @l with a new list, namely the list built
# by mapfun. If you're a C/C++ programmer, you may be wondering
# what happened to the original @l list - doesn't it need to be deallocated?
# The answer is that like Java, Perl is a modern programming language that
# does automatic memory management or "garbage collection". Scheme was the
# language used to develop this important technology. Far from just a
# convenience, memory management frees the programmer to think at a higher
# level, and gives rise to a style of programming previously considered
# impractical.
sub makehashfun
{
my $f = shift; # function to be optimized
my $hash; # local hash table to store results
sub { # new version of function
my @args = @_;
my $jargs = join ",",@args; # join multiple args into hash key
my $val = $hash->{$jargs}; # look up hash table
if ($val) {$val;} # if value exists, we're done!
else { # need to call function
$val = $f->(@args); # calls function
$hash->{$jargs} = $val; # store result in hash table
$val; # return value
} # else
} # returned subroutine of makehashfun
} # makehashfun
print "21: Now you won't have to wait 20,000 years to see ", $fib->(100), "\n";
#########################################################################