Perl Cd Bookshelf [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Perl Cd Bookshelf [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید



10.11. Prototyping Functions


10.11.1. Problem



You want to use function prototypes
so the compiler can check your argument types.

10.11.2. Solution


Perl has something of a prototype facility, but it isn't what you're
thinking. Perl's function prototypes are more like a context coercion
used to write functions that behave like some Perl built-ins, such as
push and pop.

10.11.3. Discussion


Manually checking the validity of a function's arguments can't happen
until runtime. If you make sure the function is declared before it is
used, you can tickle the compiler into using a very limited form of
prototype checking. But don't confuse Perl's function prototypes with
those found in any other language.

A Perl function prototype is zero or more spaces, backslashes, or
type characters enclosed in parentheses after the subroutine
definition or name. A backslashed type symbol means that the argument
is passed by reference, and the argument in that position must start
with that type character.

A prototype can impose context on the prototyped function's
arguments. This is done when Perl compiles your program. But this
does not always mean that Perl checks the number or type of
arguments; since a scalar prototype is like inserting a
scalar in front of just one argument, sometimes an
implicit conversion occurs instead. For example, if Perl sees
func(3, 5) for a function
prototyped as sub func
($), it will stop with a compile-time error. But
if it sees func(@array) with the same prototype,
it will merely put @array into scalar context
instead of complaining that you passed an array, but it wanted a
scalar.

This is so important that it bears repeating: don't use Perl
prototypes expecting the compiler to check type and number of
arguments for you. It does a little bit of that, sometimes, but
mostly it's about helping you type less, and sometimes to emulate the
calling and parsing conventions of built-in functions.

10.11.3.1. Omitting parentheses


Ordinarily your subroutines take a list of arguments, and you can
omit parentheses on the function call if the compiler has already
seen a declaration or definition for that
function:

@results = reverse myfunc 3, 5;

Without prototypes, this is the same as:

@results = reverse(myfunc(3, 5));

Without parentheses, Perl puts the righthand side of the subroutine
call into list context. You can use prototypes to change this
behavior. Here is a function that's prototyped to take just one
argument:

sub myfunc($);
@results = reverse myfunc 3, 5;

Now this is the same as:

@results = reverse(myfunc(3), 5);

Notice how the scalar prototype has altered the Perl parser! It grabs
only the next thing it sees, leaving what remains for whatever other
function is looking for arguments.

A void prototype like:

sub myfunc( );

will also alter the parser, causing no arguments to be passed to the
function. This works just like the time built-in.

That means that in the absence of parentheses, you cannot
know what is going on
by casual inspection. Things that
look the same can quietly behave completely differently from one
another. Consider these declarations and assignments:

sub fn0( );
sub fn1($);
sub fnN(@);
$x = fn0 + 42;
$x = fn1 + 42;
$y = fnN fn1 + 42, fn0 + 42;
$y = fnN fn0 + 42, fn1 + 42;
$z = fn1 fn1 + 42, fn1 + 42;
$z = fnN fnN + 42, fnN + 42;

Astonishingly enough, those are parsed by the Perl compiler as though
they'd been written this way:

$x = fn0( ) + 42;
$x = fn1(42);
$y = fnN(fn1(42), fn0( ) + 42);
$y = fnN(fn0( ) + 42, fn1(42));
$z = fn1(fn1(42)), fn1(42);
$z = fnN(fnN(42, fnN(42)));

Without first looking closely at the prototypes and then thinking
really hard about how Perl's parser works, you'd never be able to
predict that. Maintainability would suffer horribly.

This is one strong argument for using more parentheses than might be
demanded by purely precedential concerns (or, alternatively, this is
an argument for avoiding prototypes).

10.11.3.2. Mimicking built-ins


The other common use of prototypes is to give the convenient
pass-without-flattening behavior of built-in functions like
push and shift. When you call
push as push(@array,
1, 2, 3) the
function gets a reference to
@array instead of the actual array. This is
accomplished by backslashing the @ character in
the prototype:

sub mypush (\@@) {
my $array_ref = my @remainder = @_;
# ...
}

The \@ in the prototype says "require the first
argument to begin with an @ character, and pass it
by reference." The second @ says "the rest of the
arguments are a (possibly empty) list." A backslash in a prototype
requires that the argument actually begin with the literal type
character, which can sometimes be annoying. You can't even use the
conditional ?: construct to pick which array to
pass:

mypush( $x > 10 ? @a : @b, 3, 5 ); # WRONG

Instead, you must play games with references:

mypush( @{ $x > 10 ? \@a : \@b }, 3, 5 ); # RIGHT (but ugly)


Here's an
hpush function that works like
push, but on hashes. It uses a list of key-value
pairs to add to an existing hash, overwriting any previous values
associated with those keys.

sub hpush(\%@) {
my $href = while ( my ($k, $v) = splice(@_, 0, 2) ) {
$href->{$k} = $v;
}
}
hpush(%pieces, "queen" => 9, "rook" => 5);

You may also backslash several argument types simultaneously by using
the \[ ] notation:

sub mytie ( \[$@%&*] $; @ )

That function accepts any of the five types and passes it by
reference, followed by one mandatory scalar context argument and
optional trailing list of remaining arguments.

You can discover a particular function's prototype using the
prototype built-in function. For example, calling
prototype("hpush") given the previous definition
would return the string "\%@". You can even find
out a built-in's prototype this way—if it has one, that is. Not
all core built-ins can be emulated. For those that can, the
prototype function returns what their built-in
prototype is. Since you can always call a core built-in function like
int as CORE::int, built-ins are
deemed to reside in package CORE. For example:

for $func (qw/int reverse keys push open print/) {
printf "Prototype for %s is %s\n", $func,
prototype("CORE::$func") || "UNAVAILABLE";
}
Prototype for int is ;$
Prototype for reverse is @
Prototype for keys is \%
Prototype for push is \@@
Prototype for open is *;$@
Prototype for print is UNAVAILABLE

10.11.4. See Also


The prototype function in
perlfunc(1); the section on "Prototypes" in
Chapter 6 of Programming Perl and in
perlsub(1);
Recipe 10.5



10.10. Returning Failure10.12. Handling Exceptions




Copyright © 2003 O'Reilly & Associates. All rights reserved.

/ 875