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

I was trying to turn a terribly lame 4 line piece of code into a one liner and have hit a snag. Consider the code:
($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($machine +); if($#addrs == -1){ next; } $ipadrs = join ".", unpack('C4', $addrs[0]); @iparray = split(/\./,$ipadrs);
I was able to get it down to this:
@addrs = gethostbyname($machine) or next; @iparray = unpack('C4', $addrs[4]);
As close as I have gotten to a one liner is:
@iparray = unpack ('C4',((gethostbyname($machine))[4]));
In the above line, if the gethostbyname() function fails because the machine is not available, a warning of uninitialized value is thrown since the array being fed to unpack was never created. I was't able to get it to work using the or next; idea. I'm sure there is a way to do it, I just haven't found it. Possibly a code reference or eval though I don't know much about those. Any tip in the right direction will be greatly appreciated.

Replies are listed 'Best First'.
Re: One liner?
by jonadab (Parson) on Oct 01, 2003 at 01:11 UTC

    How much fun do you want to have with the mind of the next person who has to maintain your code?

    @ip=unpack('C4',[(sub{return(gethostbyname($machine)or next)})->()]->[4]);

    If you want it to be maintainable, leave it at two lines.


    $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
      Strangely enough, that's the point. I have been challenged by a collegue. I was told that my code was "old style" and extremely verbose. Often that is a good thing, I think. But nevertheless, I was told that I wasn't using enough of the features that perl has to offer and most importantly that I couldn't do it. So far I have been doing pretty well but I still have more to do. My collegue calls it simplifying, I call it complicating. Anyway, thanks for the thought.

        The four-line version _was_ needlessly verbose. But I don't think getting it down to one line is necessary. Two lines may be about right for this example.

        Good code is terse enough that it doesn't take forever to say anything, but not so terse that it makes the reader shake his head and drag out reference books. Somewhere in between ten-thousand-line epics and golfed-down one-liners there's a happy medium.

        Of course, you may not need the @ipaddr array at all; if you're only using the resulting numbers once, you may be able to feed the results of the second line directly into the next function. I'd have to see the larger context of your code to see how to further simplify it. But I don't think that combining your existing two lines into one will make for greater simplicity.

        If you really want to learn serious code *shortening* techniques, do a Google search for "Perl golf". But be advised that shorter code isn't always easier to read. Up to a point it is, but Perl golf has a strong tendency to go beyond that point. If you just want to scare your coworker, show him some of the stuff from the obfuscation section of Perlmonks, and tell him you're studying programming techniques from the people who wrote them.


        $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
Re: One liner?
by Aristotle (Chancellor) on Oct 01, 2003 at 09:30 UTC
    The two line version is perfect - concise and clearly readable. Nevertheless, just for the record:
    @iparray = unpack ('C4', (gethostbyname $machine) || next );

    Note that || forces scalar context on gethostbyname, in which case the address is the sole return value, rendering the slice unnecessary. Take a look at perldoc -f gethostbyname, most of Perl's functions make useful distinctions between their calling contexts.

    But since you're populating an array, I guess you're doing this in a loop. If this is the only thing in that loop, you might actually consider condensing this using map along these lines:

    my @iparray = map defined() ? join('.', unpack 'C4', $_) : (), map scalar(gethostbyname $_), @machine;

    Makeshifts last the longest.