Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

foreach loop and creating files with "$_"

by james28909 (Deacon)
on Jul 22, 2014 at 03:08 UTC ( #1094576=perlquestion: print w/replies, xml ) Need Help??

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

ok i originally answered my own question this time. i wanted to make any prespecified number of files with a foreach loop (or for loop, i dont think it matters much). I accomplished that with this code and didnt have to ask :
my $file = "file"; my $temp = "temp"; foreach (1..5){ open $file, '>', "$temp$_"; print $file "THIS IS DATA"; }

now my next question is, i have been reading around here and there about this special variable "$_" and come across this thread Here and it say to try to avoid this special variable. in the code above, it was my very first time to even try using it tbh, could someone explain to me why it should be avoided in the case of my code above? or is it ok? or what would be a better solution to it?

Replies are listed 'Best First'.
Re: foreach loop and creating files with "$_"
by xyzzy (Pilgrim) on Jul 22, 2014 at 03:58 UTC

    First one quick note:

    my $file = "file"; # why not... my $temp = "temp"; # makes sense if you later want to use # some other basename for your sequence of files foreach (1..5){ open $file, '>', "$temp$_"; # you just turned $file from a string in +to a filehandle... ... }

    In other words your assignment in the first line does nothing since the value gets clobbered when you use open

    On to your question. I quickly glanced at the node and the author is basically saying that sometimes it is unclear what $_ is supposed to represent. Whether or not this is the case depends on the context that you use it in. For instance:

    foreach (@lines) { $_ = uc; # ALL CAPS WOOOOOO s/$/!!!/; # EVERY LINE IS AN EXCLAMATION!!! print; }
    It's fairly obvious that each operation of the loop is performed on each line in @lines. However, this may not be the case if you have a longer, more complex loop, and will not work at all if you have nested loops:
    my @timesTable; foreach my $row (0..12) { # foreach my $col (0..12) { @timesTable[$row][$col] = $row * $col; } }
    There are also times where I will take advantage of $_ to perform a bunch of operations on a single variable in a sort of pseudo-loop:
    for ($leetString) { # "loops" only once s/very/uber/g; tr/astloe/457103/; s!w!\\/\\/!g; }
    You didn't hear it from me, but the best part about Perl is that once you understand how the lingo works, you can use it any way you damn please. Unless you work with people who won't understand what you're saying, and you want them to understand what you're saying. Then you have turn to these annoying things called "Best Practices".


    $,=qq.\n.;print q.\/\/____\/.,q./\ \ / / \\.,q.    /_/__.,q..
    Happy, sober, smart: pick two.
      i didnt even catch the mistake at the beginning, i was just excited it worked i guess, glad you noticed tho loool xD

        Here's some more unrequested advice:

        for (1..$num) { open my $fh, '>', sprintf "temp%02d",$_; # temp01, temp02, etc. ... }
        Consider doing this if you plan to have more than 9 temp files and you want to keep them in numerical order. %02d will pad an integer with leading zeroes until it's two digits long. For more information, consult perldoc -f sprintf. You can even use a base-10 logarithm before the loop to figure out how many leading zeroes you would need, then insert that number into your format string.

        EDIT: forgot that $_ is not implied with sprintf


        $,=qq.\n.;print q.\/\/____\/.,q./\ \ / / \\.,q.    /_/__.,q..
        "My life is like my typing: fast and full of mistakes."
Re: foreach loop and creating files with "$_"
by Athanasius (Archbishop) on Jul 22, 2014 at 03:39 UTC

    Hello james28909,

    Your use of $_ to generate the filenames “temp1”, “temp2”, ... “temp5” looks fine to me. There is, in general, nothing wrong with using $_. On the contrary, $_ is one of Perl’s strengths, because it facilitates the writing of concise, elegant code.

    In the article you reference, apotheon recommends this rule of thumb for $_: If you have to use it explicitly, use something else instead. So far as I can see (I’ve only skimmed the article), the only justification given for this opinion is that:

    a lot of people find $_ ugly and even obfuscatory.

    Possibly true, but then they are people who don’t know and use Perl! Perl is, by design, an atypical language, so its idioms and byways are often unintuitive at first, but unfamiliar should never be mistaken for ugly. As apotheon notes, people who find Perlish idioms ugly sometimes avoid Perl itself for that reason. IMHO that is not necessarily a bad thing.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      When using $_ keep in mind that it may change when you're not expecting it to. For example, if a sub gets called between setting $_ and using it, there's a fair chance that $_ will have changed. apotheon's rule of thumb is a good defence against that. Using a well named variable also has the huge advantage that it conveys intent which makes code more robust, easier to understand and easier to maintain.

      In the OP's code there is pretty much no chance that $_ is going to change unexpectedly as it stands, but for clarity a loop variable helps:

      for my $fileIdx (1 .. 5) { my $fileName = "$temp$fileIdx"; open $file, '>', fileName or die "Failed to create fileName: $!\n +"; ... }
      Perl is the programming world's equivalent of English

      a lot of people find $_ ugly and even obfuscatory.

      Possibly true, but then they are people who donít know and use Perl! Perl is, by design, an atypical language, so its idioms and byways are often unintuitive at first, but unfamiliar should never be mistaken for ugly. As apotheon notes, people who find Perlish idioms ugly sometimes avoid Perl itself for that reason. IMHO that is not necessarily a bad thing.

      I concur. And I'll submit that $_ is actually quite intuitive -- it's just other programming languages that are unintuitive, unfamiliar and (arguably) unnatural in this regard.

      For me, $_ is simply the current topic that's being talked about. Like in natural languages, sometimes you leave it out entirely, since it's clear from the context what's being meant; sometimes you merely say "this" or "it" (which is how I read $_). Perl doesn't go quite as far as natural languages where you can have several of these at once, but on the other hand, unlike natural languages, Perl cannot afford to be ambiguous ("Bill met Bob. They talked, and he gave him back the book" is fine; the same thing in Perl wouldn't be).

      Still, having one "topic" is better than having none, as most programming languages do. Natural languages are intuitive, after all, and we're all familiar with them.

        For me, $_ is simply the current topic that's being talked about.

        well, I think it is the problem. how about 2 topics happen at the same time? actually it's the biggest problem about $_ . think a topic you want to talk is the another topic (but happen at the same time).




        I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction

      a lot of people find $_ ugly and even obfuscatory... Possibly true
      It's about as true as the statement "Chinese language is ugly and obfuscatory". I mean, a lot of people find things like "舍阿奈比山是突尼西亞最高峰" completely unreadable... right?
      actually perl's wild looking code is what brought me to perl, i like its hacky script look lol
Re: foreach loop and creating files with "$_"
by wjw (Priest) on Jul 22, 2014 at 03:51 UTC

    Take a look at $_ in perlvar. Frankly, I don't hesitate to use $_. But then most of my code is fairly straight forward procedural code without much scoping challenge. I am sure someone here has run into an issue or two making use of $_, but I certainly have not.

    I personally don't like the 'visual' of $temp$_, so would handle it differently, but I don't see anything functionally wrong with it.

    ...the majority is always wrong, and always the last to know about it...

    Insanity: Doing the same thing over and over again and expecting different results...

    A solution is nothing more than a clearly stated problem...otherwise, the problem is not a problem, it is a facct

      just as an example, how would you do it differently if i might ask? :)
        my $file = "file"; my $temp = "temp"; foreach (1..5){ open $file, '>', "$temp" . "$_"; print $file "THIS IS DATA"; }
        or
        my $file = "file"; my $temp = "temp"; foreach my $end (1..5) { open $file, '>', "$temp" . "$end"; print $file "THIS IS DATA"; }
        (not tested)

        Personal preference only...

        ...the majority is always wrong, and always the last to know about it...

        Insanity: Doing the same thing over and over again and expecting different results...

        A solution is nothing more than a clearly stated problem...otherwise, the problem is not a problem, it is a facct

        First with $_ then without

        use Path::Tiny qw/ cwd path tempfile /; ## in cwd path( "temp$_" )->spew("THIS IS DATA") for 1 .. 5; ## in $TMP directory tempfile( 'tempjamesXXXX' )->spew("THIS IS DATA") for 1 .. 5;
Re: foreach loop and creating files with "$_"
by Anonymous Monk on Jul 22, 2014 at 03:59 UTC
Re: foreach loop and creating files with "$_"
by james28909 (Deacon) on Jul 22, 2014 at 04:51 UTC
    here is another example:
    open my $data, '<', "data.bin"; binmode($data); read $data, my $buf, 0x1000000; my $copy = "copy"; foreach (1..5){ open my $file, '>', "$copy$_"; binmode($file); print $file $buf; }

    thats so easy now that i think about it. hell i use the foreach loop alot in some other code i have. its a wonder i didnt think about this before now, anyway thanks for the input :) you could even make it read in so many chunks at a time or split up the file in other words (why? i dunno):
    open my $data, '<', "data.bin"; binmode($data); my $chunk = "chunk"; foreach (1..5){ open my $file, '>', "$chunk$_"; binmode($file); read $flash, my $buf, 0x200; print $file $buf; }
Re: foreach loop and creating files with "$_"
by AnomalousMonk (Bishop) on Jul 22, 2014 at 16:46 UTC
    my $file = "file"; my $temp = "temp"; foreach (1..5){ open $file, '>', "$temp$_"; print $file "THIS IS DATA"; }

    Whatever you may think of the use of  $_ this code doesn't compile under strictures because  $file is not undefined. Quoth the open docs (emphasis added):

    If FILEHANDLE is an undefined scalar variable (or array or hash
    element), a new filehandle is autovivified, meaning that the
    variable is assigned a reference to a newly allocated anonymous
    filehandle. Otherwise if FILEHANDLE is an expression, its value
    is the real filehandle. (This is considered a symbolic
    reference, so "use strict "refs"" should *not* be in effect.)

    c:\@Work\Perl\monks\james28909>perl -wMstrict -e "my $file = 'file'; my $temp = 'temp'; ;; foreach (1..5){ open $file, '>', qq{$temp$_}; print $file 'THIS IS DATA'; } " Can't use string ("file") as a symbol ref while "strict refs" in use a +t -e line 1.

    (Talk about rearranging the deck chairs on the Titanic! :)

      yeah i had caught that earlier lol. i removed the first reference to the "file" and added "my $file" in the loop.

      and thanks to everyone for the valuable input.

      need more input hehe
Re: foreach loop and creating files with "$_"
by sundialsvc4 (Abbot) on Jul 22, 2014 at 12:06 UTC

    I’d go with foreach my $var (1..5) every time, for the very same reason that “I don’t like to see the same global variable-name being used again and again.”   Especially not, as in this case, one that you can’t see.

    Okay, so it works okay now.   Goody,goody, but then someone comes along and needs to make a truly-slight change ... which just happens to consist of some other control-structure that changes the meaning and value of $_.   Now, they have to dance-around your program, making a bunch of source-code changes (entirely unrelated to what they wanted to do ...), just to do what you could well have done the first time.   (And, to keep things realistic, let’s say they inadvertantly miss just one thing ...)   Meh.   That sort of thing is “just not worth the clever.”

    Keep it simple ... keep it maintainable.   I’m not laying-down any sort of commandment here, but it is trivially easy (as shown) to create a very nice, named, locally-scoped variable, associated with the loop and existing only within its scope, that contains the value of the loop index.   That’s one of Perl’s nicest, most pragmatically useful features.   (As is $_, yes, in small doses ...)

      I think the biggest maintainability problem with $_ is accepting it as a default without using it. We are forced to remember every place it can be used in this way.
      Bill

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1094576]
Approved by Athanasius
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2021-05-14 17:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Perl 7 will be out ...





    Results (150 votes). Check out past polls.

    Notices?