Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

built-ins sometimes iterating over a list, sometimes not

by dragonchild (Archbishop)
on Nov 08, 2001 at 01:01 UTC ( [id://123920]=perlmeditation: print w/replies, xml ) Need Help??

This is a quasi-rant, quasi-question, quasi-me-being-bored-at-work node. So, you've been warned. *grins*

I was just working on a script that uses Getopt::Long and has something like 4 mandatory parameters and some 6 optional ones. Well, if I don't have any of the mandatory ones, I need to print the correct usage, then fail. Easy enough, right? Just do something like:

&printUsage && exit unless exists @{$args}{@mandatory_args};
NOPE! ... exists doesn't work on a list, no matter how you slice it. Instead, you have to do something like:
&printUsage && exit if grep !$_, map { exists $args->{$_} } qw(user to_db to_sid to_site);
That just looks ugly. Anyone else run into this?

------
We are the carpenters and bricklayers of the Information Age.

Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Replies are listed 'Best First'.
Re: built-ins sometimes iterating over a list, sometimes not
by danger (Priest) on Nov 08, 2001 at 02:47 UTC

    One way to simplify your test is to get rid of the extra map:

    Usage() && exit if grep{not exists $args->{$_}}@mandatory;

    An alternate scenario could involve Getopt::Declare, which allows you to specify if an option is required or not (negating the need for this particular test). For example:

    #!/usr/bin/perl -w use strict; use Getopt::Declare; my $args = Getopt::Declare->new(<<'SPEC'); -x <x:n> x coord (real) [required] -y <y:n> y coord (real) [required] -z <z:n> z coord (real) {print "Processing in three dimensions\n"} SPEC #rest of program... __END__

    Running the above without -x or -y options causes an error message regarding the missing option. For free you also get -help which will print out a usage statement (for free you'll also get a slight speed hit on loading the module). Note: there are hard tabs between the option specs on the left and the option descriptions on the right (see the docs for formal specification requirements).

Re (tilly) 1: built-ins sometimes iterating over a list, sometimes not
by tilly (Archbishop) on Nov 08, 2001 at 03:01 UTC
    exists on a slice, I never thought of trying it, but it certainly sounds like a nice feature request. (No idea how easy it is though, but you can always ask.)

    But the hideous line is worse than needed. Just do this:

    if (grep !exists $args->{$_} @mandatory_args) { printUsage() and exit; }
    which looks much better. No need to map and then grep, just grep!

    Now for a few stylistic points.

    1. I don't like nesting logical assertions to save lines. Combining && and if used for program flow on one line is hard on maintainance programmers, break it up.
    2. Of the various ways of calling a function, the only one to avoid is &foo. Unless you need the implicit parameter passing, avoid it. You don't want someone picking up the habit without knowing about the gotcha.
    3. End of line indentation is the only kind of indentation I object to. As pointed out in Code Complete, it results in very rapidly getting deep nesting, it breaks the easily seen connection between depth of indentation and depth of logic, and it represents a stylistic maintainance problem; you can't edit the code without unnecessary playing around with indentation on following lines.
    4. I returned to using an array of mandatory arguments in the test in the hope that you could reuse that array in the usage statement. If possible, it is nice to have your usage statement automatically synchronized with your test for correct usage. The rest of the details, in fine mathematical tradition*, are left as an exercise.
    BTW if you can figure out a good way to handle the last point, I am all ears. This is a variant on the problem I addressed at Re (tilly) 2: passing subroutine arguments directly into a hash. The solution I outline there works, but the messages are inappropriate for a user interface, rather it is an internal interface to help programmers debug.

    * According to tradition, when mathematicians come to a point they need in a text, whose proof they can't remember, they make it into an exercise for the reader. :-)

      Ok. So, I'm stupid about using the extra map. I'm also stupid about using the wrong invocation of the function. I'm so used to method calls which don't need the & or the () that I forgot which is supposed to be used when calling a 'normal' function. :-)

      I find that nesting logical assertions helps the reader understand what a string of maps and greps and sorts is doing. Yes, in theory, the programmer is supposed to use intermediate variables, but no other language has the same piping that Perl has, so Code Complete never addressed the issue. I've seen merlyn and theDamian both use a similar form to do the Orcish manauver or Schwartzian transforms. *shrugs*

      As for mandatory args ... *sighs* This is similar to another problem I have that I posted somewhere, but don't have the motivation to find.

      *ponders* Wouldn't something like the following work?

      sub printUsage { my ($mandatory, $optional) = @_; print "Usage: $0\n"; print "-$_ ", uc $_, " " for @$mandatory; print $/; print "[-$_ ", uc $_, "]" for @$optional; print $/; }

      Of course, the formatting isn't as good. I'll leave that as an exercise for the reader. *grins*

      Update: I changed how I did the calls into Getopt::Long. Please critique.

      my @mandatory = qw(user to_db to_sid to_site); my @optional = qw(password vendor type model); my @default = qw(from_db from_sid); our $from_db = "some_db"; our $from_sid = "some_sid"; my $args = {}; my $rc; { no strict 'refs'; $rc = GetOptions($args, (map { "${_}=s" } @mandatory), (map { "${_}:s" } @optional), (map { ("${_}=s", \${"$_"}) } @default), ); } printUsage(\@mandatory, \@optional, \@default) unless $rc; printUsage(\@mandatory, \@optional, \@default) if grep !exists $args->{$_}, @mandatory;

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Actually Code Complete does address this exact issue. I don't have it in front of you so I can't give page numbers, but they talk about the importance of not reducing lines of code by putting more on one line. I think it was talked about with particular reference to C's trinary operator.

        As for the question of whether nesting logic helps or hurts, my experience is very strongly that it hurts my comprehension. What I find best for handling nested maps, greps, and sorts is to put them on separate lines, indented, to be read from bottom to top. That is the same strategy with any complex control logic. If it is a single "thought" in your program, make it into line-noise. If it is central to how your program works, break it visually along the lines of how it is to be understood.

        I have seen similar styles from other programmers. Including both merlyn and TheDamian. Again, I sometimes break out logic, sometimes don't. But if it is central to what I am doing, I break it out. Likewise if I start getting multiple control statements on the same line and want to reach for && as a control statement, I break it out.

        As for the question of whether other languages have the piping that Perl does, I disagree. Here is a Schwartzian sort of a pipe-delimited list on the 3'rd then 5'th columns (ascending then descending) in Perl:

        my @sorted = map {join, "|", @$_} sort {$a->[2] cmp $b->[2] or $a->[4] cmp $b->[4]} map {[split /\|/, $_, -1]} @my_list;
        Here is how you do the same thing in Ruby:
        sorted = my_list.map { |i| [ i.split(/\|/)[2,4], i ] }.sort.map { |i| i[-1] }
        Do you see? Chaining method calls achieves exactly the same effect as Perl's pipelining does, and it even has the benefit of reading more naturally left to right! So the formatting issue is hardly unique to Perl...
Re: built-ins sometimes iterating over a list, sometimes not
by Aristotle (Chancellor) on Nov 08, 2001 at 17:43 UTC
    That is why I love the statement modifier form of for. :)
    OPTION_CHECK: { last OPTION_CHECK if exists $args->{$_} for @mandatory_args; printUsage() and exit(); }
    Doesn't that read so beautifully descriptive? :)
      yes! exactly! isn't that what for is for?
Re: built-ins sometimes iterating over a list, sometimes not
by chipmunk (Parson) on Nov 08, 2001 at 09:48 UTC
    The main part of your question has already been answered, so...

    Is there a reason you don't want to exit if printUsage() happens to return false? I think you meant: printUsage(), exit if whatever; :)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://123920]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2024-04-19 22:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found