First off, please take the time to use
<ol><li></li></ol> tags for lists like that (its much easier to read), and please use
<code> tags so you dont get weird traslations of code...
So lets take a slightly changed version of the code:
use Carp;
sub make_tr {
my $from=quotemeta(shift);
my $to =quotemeta(shift);
my $opts=shift || "";
Carp::croak "Bad option in '$opts'" if $opts!~/[cds]/;
my $eval_str="sub { (@_ ? \$_[0] : \$_)=~tr/$from/$to/$opts}";
my $tr=eval $eval_str;
die "While evaling\n$eval_str\n$@" if $@;
return $tr;
}
-
So what does the quotemeta(shift) do? Well, shift removes the first value from an array and returns it. If it isnt provided a specific array to operate on then it uses @_ or @ARGV, depending on whether it is called inside a subroutine or not. Quotemeta is a handy little function for turning strings with weird (meta) characters in them (anything that isnt a word character I think) into a backslashed version. We use it because if the symbols you wanted to translate were the same as used in the tr/// function (ie '/') then it would break the code, so once quotemeta'ed something like "a/b" would become "a\/b" and not break the code.
As for the local() bit, I think the safest thing for me to say is that you should read up on my() and local() in perlfunc. They arent the same thing, and if you are getting into the habit of using local like that then you need to get out of it pretty fast. About %99.999 of the time beginners use local() they really wanted my(). Have a read of Coping With Scoping for an indepth review of the issues.
-
Ok the code you mention has been replaced with what I consider to be a smarter solution. The original idea was to delete any characters that arent allowed to be options for a tr///, as well as to lowercase CDS as a convienience. Apon reflection I think this was a bad idea as the calling programmer might not realize that this had happened and wonder why such weird things were happening. The replacement simply throws an exception if an illegal character were passed in. I think this is more useful and intuitive than my first approach. And if your question also refered to the shift || "" then it means shift a value off of @_, if that value is in any way false (ie "", 0 or undef) then replace it with "" which is a perl idiom for converting undef values to something harmless (warningless actually) like "" (or to a default value). Normally (until perl 5.10 introduces the // operator and its cousin =//) you have to be careful about this idiom because there are three different forms of false and most often we are only concerned with undef and dont want to change the others. However in this case '0' is a totally illegal value that we can more or less safely silently convert, and "" would convert to itself, so we dont have a problem.
-
my $tr=eval "sub { (defined \$_[0] ? \$_[0] : $_)=~tr/$from/$to/$opts}";
Ok, i can see why this one threw you for a loop. (I simplified it in the above code.) Basically it generates an anoymous subroutine to do the desired tr/// as it is assumed that you will want to do a given tr/// more than once. In more detail the first thing to understand is that the regex binding operators =~ and !~ apply to any lvalue. They dont just apply to scalars/strings. So what the (defined $_[0] ? $_[0] : $_) or the (@_ ? $_[0] : $_) do is to check to see whether an argument has been passed to the subroutine. (Its called the ternary operator, its like a funny looking if statement that returns a value. Syntax: CONDITION ? TRURETURN : FALSERETURN ) If one has been passed then it is used (via $_[0]) for the tr/// and if one hasnt then $_ is used. Since both of these values are lvalues we can bind this entire thing to the regex. (it is precisely for this kind of thing that we have the ternary operator, we could do the same thing with an if, but code would be duplicated.) Its important to understand that the actual elements of the @_ array are special. They are aliases to the passed variable, not copies or references. (Well, actually an alias is like a funny kind of reference that doesnt need to be dereferenced.) So that part determines which variable the tr/// affects. The tr/// part is pretty straight forward.
Now why is $_[0] backslashed and the $from is not? Well, because we using string interpolation to build up a piece of code that defines a subroutine that we then return. The $_[0] needs to be literally in the code, but we want the $from, $to and $opts to be trnsformed into their contents, the leading \ inside a string keeps a variable from being interpolated. (This is why I changed the error statement from the eval so that you can see the actual code that caused the error)
- Well, I wouldnt do that. What would happen in an error situation is that no new subroutine would be generated and when you tried to do &$tr or $tr->(); an exception would be thrown as the value $tr would be undefined.
May i suggest you get yourself a username and come hang out. It sounds like theres stuff you could learn....
HTH
--- demerphq
my friends call me, usually because I'm late....
Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
Read Where should I post X? if you're not absolutely sure you're posting in the right place.
Please read these before you post! —
Posts may use any of the Perl Monks Approved HTML tags:
- a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
| |
For: |
|
Use: |
| & | | & |
| < | | < |
| > | | > |
| [ | | [ |
| ] | | ] |
Link using PerlMonks shortcuts! What shortcuts can I use for linking?
See Writeup Formatting Tips and other pages linked from there for more info.