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

I am reading through some Tiny modules to learn some more about perl.

I'm having trouble understanding the following line of code, if anyone could help me understand what is said here :

$buff .= $_ for  "\n", '  ' x ( $level - 1 ), shift @pre_push;

Replies are listed 'Best First'.
Re: How do I read this line of code?
by choroba (Cardinal) on Sep 29, 2011 at 09:17 UTC
    This adds a string to $buff. In fact, it adds several things: a newline, $level-1 spaces and the first element of @pre_push, that is removed from the array. It is equivalent to
    for my $string ("\n", ' ' x ( $level - 1 ), shift @pre_push) { $buff = $buff . $string; }

      TIMTOWTDI. I would never use a for loop for this, but it might well be a simplification of what in earlier versions warranted the for loop.

      $buff .= $_ for "\n", ' ' x ( $level - 1 ), shift @pre_push; => { my $indent = " " x ($level - 1); $buf .= "\n" . $indent . shift @pre_push; } or $buff .= "\n" . (" " x ($level - 1)) . shift @pre_push; or $buff .= join "" => "\n", " " x ($level - 1), shift @pre_push; or $buff .= join ((" " x ($level - 1)) => "\n", shift @pre_push); or ...

      Enjoy, Have FUN! H.Merijn

        Okay, this clarifies a lot, so there is no real need for a for loop.

        The for loop is not used to loop through the elements of the array, but rather to loop through the things we want to add to $buff being:

        1. a newline
        2. spaces
        3. the entire content of the array

      Thanks for your answer, this helps a lot.

      Now, what if there is no shift:

      $buff .= $_ for "\n", '  ' x ( $level - 1), @pre_push;

      I would expect every element in @pre_push to have a newline and spaces in front of it, but when I test it, using the following code, I see that the newline and spaces are only added once.

      my @pre_push; my $level = 2; my $buff = ''; push (@pre_push, "test"); push (@pre_push, "test2"); for my $string ("\n", ' ' x ( $level - 1 ), @pre_push) { $buff = $buff . $string; } print $buff;

      Output:

        testtest2

        The for (used as a statement modifier) in the original line of code takes a list which consists of three entries: newline, some number of spaces, and an item shifted off an array. If you remove the shift the last entry in the list becomes another list (the elements in the array) appended to the list containing the first two items. There is nothing there to prefix each element following the first two elements of the list with the concatenation of the first two elements which is what your expectation implies.

        If you want to achieve that you have to rewrite the statement a little. Consider:

        my @pre_push = qw(test test2); my $level = 2; my $buff = ''; $buff .= $_ for map {"\n" . (' ' x ($level - 1)) . $_} @pre_push; print $buff;

        Prints:

        test test2

        The map concatenates the prefix to each element of the array before passing the modified list on to the for.

        Alternatively you could move the prefix concatenation to the assignment to the left of the for to give the same result and remove the map:

        $buff .= "\n" . (' ' x ( $level - 1)) . $_ for @pre_push;
        True laziness is hard work
        The loop iterates over the newline, spaces and the array. If you want to iterate it over the array and add newlines fore each member of the array, you can do
        $buff .= "\n" . (' ' x ($level - 1)) . $_ for @pre_push;
Re: How do I read this line of code?
by Anonymous Monk on Sep 29, 2011 at 11:44 UTC
    As soon as you do figure out what this "clever one-liner" is supposed to do, and as soon as you verify what it is actually doing (evil bugs often lurk in clever places...) rewrite the damm thing in a few lines of well-documented code.