views:

117

answers:

4

I don't know the slightest bit of Perl and have to fix a bug in a Perl script.

Given a variable $myvar which contains a string, if the first character is a dot, replace it with "foo/bar".

How can I do this?
(Bonus points if you can guess the bug)

+6  A: 
$myvar =~ s+^\.+foo/bar+ ;
Benoit
The + are alternative regex quote characters because the normal "/" conflicts with "foo/bar". You can choose what you want there, I personally find brackets more readable: s{^\.}{foo/bar}.
Thilo
does this replace all dots or only the first one?
Arian
Only the one at the very beginning of the string (this is what the ^ does)
Thilo
Only the first one. See http://perldoc.perl.org/perlre.html for perl regular expressions. ^ matches only at start of a string.
Benoit
since pluses are part of regex syntax it seems like a poor choice. the presence of `\.+` appears to indicate that the regex replaces all the dots, even though it doesn't.
flies
+5  A: 

Some substr magic:

$_ eq '.' and $_ = "foo/bar" for substr $myvar, 0, 1;

And this syntax makes me love perl 5.12

for(substr($myvar, 0, 1)) {
    when('.') { $_ = "foo/bar" }
}
gugod
O.o I really have to learn perl...
Arian
A nice and creative combination of lvalue substr, aliasing, and smart-matching. I like it! (and still would've used the obvious and easy to understand regexp replacement instead) :-)
rafl
Or alternatively, an example of everything that is wrong with Perl. 'For' is for looping. If in order to use some other goodies you need to use 'for' even though you're not looping, then the language is broken.
Colin Fine
Although a Perl-lover by trade and inclination, I have to agree with @Colin. `for` is for looping. Shoehorning it into something like this instead of using `if` (as eugene y did) just seems like obfuscation purely for the sake of being too clever by half.
Dave Sherohman
I don't need to use `for`, it's just a (remotely) funny option.
gugod
@Colin Fine: There is nothing like `for` is for looping. `for` is for doing something for. Try read it like `do { something } for something;` and it makes perfect sense. Same apply to `for (something) do { something }`. Perl is not C or Java or ... It is language to express yourself. It is language made by linguist.
Hynek -Pichi- Vychodil
If you're going to bring linguistics into it, then on the contrary, it makes no sense at all. English has an idiom "for all/every/each" but it does not use "for" (on its own) in that way. "Do x for y" in English can only mean 'for the benefit of y', and y need not be in any way involved in x. It is only our Programming experience which leads us to attach any meaning at all to these expressions.
Colin Fine
+5  A: 

You can use substr:

 substr($myvar, 0, 1, "foo/bar") if "." eq substr($myvar, 0, 1);
eugene y
+1 for readability. Propably the only example a non-perler would understand.
Arian
@Arian: I don't think so. Using substr on the LHS of an assignment is also extremely Perlish...
Thilo
Yes, it is unusual (for programmers of other languages), but the intention is quite clear.
Arian
@eugene +1 You made the edit just as I was running the benchmarks ;-) Upon looking at the results, I think it is OK to use `index/substr` as well as `substr/substr` given that the set of input strings will probably be a mix.
Sinan Ünür
@eugene I cleaned up my comments and added a CW answer with benchmarks.
Sinan Ünür
+1  A: 

Inspired by the discussion on @eugene's answer, here are some micro-benchmarks using ActiveState perl 5.10.1 on Windows XP. Of course, my benchmarks suck, so take it with a spoonful of salt.

#!/usr/bin/perl

use strict; use warnings;

use Benchmark qw( cmpthese );

my $x = 'x' x 100;
my $y = '.' . $x;

for my $s ($x, $y) {
    printf "%33.33s ...\n\n", $s;
    cmpthese -5, {
        's///' => sub {
            my $z = $s;
            $z =~ s{^\.}{foo/bar};
        },
        'index/substr' => sub {
            my $z = $s;
            if (0 == index $z, '.') {
                substr($z, 0, 1, 'foo/bar');
            }
        },
        'substr/substr' => sub {
            my $z = $s;
            if ('.' eq substr $z, 0, 1) {
                substr($z, 0, 1, 'foo/bar');
            }
        },
    };
    print '=' x 40, "\n";
}

Output:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

                   Rate  index/substr substr/substr          s///
index/substr  1622404/s            --          -14%          -42%
substr/substr 1890621/s           17%            --          -32%
s///          2798715/s           73%           48%            --
========================================

.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

                  Rate          s/// substr/substr  index/substr
s///          367767/s            --          -57%          -62%
substr/substr 857083/s          133%            --          -10%
index/substr  956428/s          160%           12%            --
========================================
Sinan Ünür