Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

String concatenation

by Anonymous Monk
on Apr 11, 2012 at 19:42 UTC ( [id://964608]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

In string concatenation which is preferred?
$string = $var1 . $var2 . $var3; $string = "${var1}${var2}${var3}"; $string = "$var1$var2$var3"; $string = join('', $var1,$var2,$var3);

Replies are listed 'Best First'.
Re: String concatenation (join++)
by tye (Sage) on Apr 11, 2012 at 20:41 UTC

    Perhaps if you use Acme::ESP, you could see what the strings think about it (which they prefer).

    For your (contrived?) example, there's really very little to hang a preference on. And you left out:

    $string = sprintf "%s%s%s", $var1, $var2, $var3;

    But if the nature of the concatenation is such that it is likely to become more complicated later on (a case I very often run into), then I'll usually go with join as, after years of doing a lot of concatenating of strings, join() was the one method that scaled the best (by far).

    For the exact case you show, I'd just do "$var1$var2$var3", though. I only resort to ${var1} for cases like "${var1}'s ...", and even then I'll often go with an alternative like "$var1\'s ...".

    I've come to quite dislike extensive use of . (Perl5's string concatenation operator). It has the wrong precedence for so many things so I'm pretty unlikely to use more than one . in any given expression. Once I've got two .s, there's likely to eventually be more and then I'll want 1+$index in there:

    % perl -e'print "found " . 1+2 . " items" . $/' 2 items

    The above is not a pleasant thing to stumble over, especially when the evidence is further separated from the source.

    I'm not bothered by "Found 1+$index items" not doing the addition because it doesn't look at all like it should (to me, at least). But I find ... . $x+$y . ... or even ... . 0+@items . ... to be pitfalls. I've become somewhat attuned to spot such a mistake, but it can still slip past me. So I mostly just spot "over-use of ." as a problem in itself instead.

    Note that .= doesn't have that problem (and can be quite efficient), so I often use it (when appropriate).

    And I rarely use sprintf, in part because I dislike the "correspondence at a distance" problem between the format specs and the values. (Also because I've seen too many bugs due to sprintf "...$var...", ....) I'm more likely to end up with something like:

    join '', ..., sprintf( "%6.2f", $amount ), ..., ;

    But just yesterday I started to write:

    join '', $cmp, " '", $count, " ", $unit, "'::interval";

    (except two of the three scalars were moderately complex expressions) but almost immediately realized that this was an exception to my learned preference. So, before I even had much of it typed in, I changed it to the much clearer:

    sprintf "%s '%d %s'::interval", ...;

    "$cmp '$count $unit'::interval" would have been even better except that $cmp and $unit were actually too complex to leave such readable (but not complex enough to really warrant their own line and own variable).

    Going to a further extreme, you end up with templating systems. But my experience is that most things that start out as even pretty complicated concatenations don't end up turning into something that fits well into a templating system. Heck, at my current job I am often taking things that are being done in the templating system and replacing them with mostly join in the process of making the code clearer and easier to maintain. I find templating systems are best at cases of a lot of hard-coded text with relatively infrequent replacements. And the volume of the hard-coded part has to be pretty large before real templating pays off over join().

    And I'll just spare everybody my thoughts on how bad here-docs are for the vast majority of cases. :)

    - tye        

      You forgot one more method of concatenation via substr.


      1. substr($a, length($a), 0, $b);
      or
      2. substr($a, length($a)) = $b;

      This concatenation is faster for very long strings.

      For string size 10 - 100 symbols fastest way is $a . $b and join

      For string size ~1 Mb fastest ways join, substr and $a.$b

      For strings size ~1Gb :) fastest ways are substr, strange method "@{[$a, $b]}" :) and join

      Join has good speed for all variants.

Re: String concatenation
by Riales (Hermit) on Apr 11, 2012 at 20:21 UTC

    This is probably a case of unnecessary optimization, but here are benchmark results for those interested:

    s/iter join dot interpolate1 interpolate +2 join 2.22 -- -24% -25% -32 +% dot 1.69 31% -- -1% -11 +% interpolate1 1.67 33% 1% -- -10 +% interpolate2 1.50 48% 13% 12% - +-

    And here's the code that generated this. I had to loop a bunch to get any kind of result. Not sure if this has any kind of effect on anything.

    use strict; use warnings; use Benchmark qw/:all/; my $string; my ($var1, $var2, $var3) = ('asdf', 'zxcv', 'qwer'); cmpthese(4, { dot => sub { $string = $var1 . $var2 . $var3 for (0 .. 10000000); }, interpolate1 => sub { $string = "${var1}${var2}${var3}" for (0 .. 10000000); }, interpolate2 => sub { $string = "$var1$var2$var3" for (0 .. 10000000); }, join => sub { $string = join('', $var1, $var2, $var3) for (0 .. 10000000); }, });

      Both of the interpolation forms compile to the same optree. Perl does the same work for both of them. Any difference between their benchmarks is noise.

      Given that, the time difference between concatenation and interpolation (which do have different optrees) is insignificant.

        In fact it goes further than that; the dot form also compiles to the same optree as the interpolation forms.

        Dave.

        Ah, yes--I ran my script a couple more times and it seems that the two interpolates and the dot are pretty much the same but the join is consistently slower. I envy the monks with an understanding of what Perl does internally!

Re: String concatenation
by petdance (Parson) on Apr 11, 2012 at 21:04 UTC
    Use whichever most clearly shows the intent of your code. I can see cases for concatenation and interpolation and join().

    For example, I prefer the first one in this example. I think it feels more like reading prose.

    print "Action status: $status"; # Preferred by me print 'Action status: ' . $status;

    But say you're building a string to show the time. I think the first highlights the elements that are being used to build $date.

    $date = join( '/', $day, $month, $year ); # Preferred by me $date = "$day/$month/$year";

    And sometimes it doesn't make sense to interpolate or join and you just concatenate.

    $response = $head . $html_opening . $body . $html_closing; # preferred $response = join( '', $head, $html_opening, $body, $html_closing );
    Bottom line: Go for what is most expressive, and makes the code clearest to the reader. Assume that any microseconds you shave will be insignificant.

    xoxo,
    Andy

      Thanks to all, Andy I agree entirely. I've been slapped on the hand for not being consistent but since speed has little difference between the various methods readability is the key. Being consistently readable is the ideal. Choosing one method and sticking with it is nonsensically dogmatic. I just needed a sounding board, sorry if it seemed trivial. I can now fight my corner with a little more confidence. Thanks Monks.
Re: String concatenation
by bart (Canon) on Apr 11, 2012 at 20:14 UTC
    What is it these days with all these micro-optimization posts?

    Whatever you use, it probably won't make any significant difference, because even the slowest of the lot will most probably be much faster still than the rest of your code.

    And you can always test using Benchmark.

      I would prefer the first method.
Re: String concatenation
by cursion (Pilgrim) on Apr 11, 2012 at 20:01 UTC

    I'd vote on the first or third, but the way the rest of the code/project does it should be the winner.

    Also, could depend on how you use $string later, maybe you don't need to concat here.

Re: String concatenation
by snape (Pilgrim) on Apr 11, 2012 at 20:11 UTC
    $string = $var1 . $var2 . $var3;

    This is commonly used in Perl. It could be used during the print statements too.

    $string = "${var1}${var2}${var3}"; $string = "$var1$var2$var3";

    It is essentially the same thing. There is no difference between the two statements.You need to keep in mind that the following statement gives you a "string".

    $string = join('', $var1,$var2,$var3);

    Join is usually used when you need to convert an array to a string.

Re: String concatenation
by Anonymous Monk on Apr 11, 2012 at 20:36 UTC

    I prefer

    $string = $var1 . "${var2}" . join('', $var3);

Re: String concatenation
by GrandFather (Saint) on Apr 12, 2012 at 02:49 UTC

    I'd be much more worried by identifiers of the form $fooN - use an array instead.

    Assuming that you really intended $foo, $bar and $baz as the variable names I'd suggest the interpolation variants (two and three, without {} except where required) in most cases. Context may dictate that the join fits better with the surrounding code at times though. Because of double quote interpolation and Perl's general string handling power the concatenation operator is seldom used so the first variant is least likely to be used.

    True laziness is hard work
Re: String concatenation
by ww (Archbishop) on Apr 11, 2012 at 22:27 UTC

    Preferred (by /me, YMMV): 1 or 4, depending on circumstance.

    Tolerable (same qualifier), but barely: 3

    But, 2 provokes an instant negative reaction. I'm sure wiser heads see ways that this is preferred to 1, but IMO, it's an invitation to some future maintainer to misread that as dereferencing and chase wild geese. In short, it looks to me like an extra keystroke whose chief impact is obscuring the code.

Re: String concatenation
by VinsWorldcom (Prior) on Apr 11, 2012 at 20:05 UTC

    I usually use 1, or maybe 2 if there is other 'stuff' I'm putting in $string other than just $var1/2/3. Personal preference I guess.

    Are you concerned with speed / overhead of one operation versus another? I'm guessing that micro-optimization won't make much difference - although I haven't benchmarked, so don't know for sure.

Re: String concatenation
by Boldra (Deacon) on Apr 12, 2012 at 14:21 UTC
    Of course, there's also:
    @vars = $var1,$var2,$var3; undef $"; $string = "@vars";
    and
    undef $"; $string = "@{ [ $var1, $var2, $var3 ] }";
    which might also be appropriate :P
Re: String concatenation
by Anonymous Monk on Apr 12, 2012 at 00:20 UTC

    Neither :)

    push @string, $var1, $var2, $var3; return join '', @string;

      From the "I'm afraid of lists... sometimes!" department?

Re: String concatenation
by brx (Pilgrim) on Apr 13, 2012 at 12:24 UTC
    Cool, a TIMTOWTDI-game :o)

    $string.= $_ for ($var1,$var2,$var3);

    $;='';my%h;$h{$var1,$var2,$var3}=$;;($string)=%h;

Re: String concatenation
by Anonymous Monk on Apr 16, 2012 at 23:01 UTC
    A while back I wondered the same thing, and ended up benchmarking a number of approaches:
    interpolation: $result = "$hello $world"; concatenation: $result = $hello.' '.$world; reverse-concat: $result = $hello.(' '.$world); interp-concat: $result = "$hello ".$world; concat-interp: $result = $hello." $world"; sprintf: $result = sprintf("%s %s", $hello, $world); join: $result = join(' ', $hello, $world); super-join: $result = join('', $hello, ' ', $world);
    The results, fastest first:
    reverse-concat: 14.24 interp-concat: 14.43 concat-interp: 14.68 interpolation: 14.98 concatenation: 15.74 super-join: 17.65 join: 17.68 sprintf: 24.65
    And I concluded that I would never use sprintf for this, and there really wasn't much difference between the other approaches, so I should just focus on what was easiest for someone to understand in any given context.
Re: String concatenation
by shawnhcorey (Friar) on Apr 17, 2012 at 13:23 UTC

    Sigh

    Speed is irrelevant. What is more readable? Personally, I would prefer seeing the string interpolation or the sprintf. However, you opinion may vary. :)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://964608]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-03-28 15:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found