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

I'm writing a program that I would like to have use a succint and easy to read rule format. The solution I cam up with makes -w scream bloody murder, though. Is there anything wrong with this approach? Any better ideas?

I just love to play with the ?: stuff! I tried several if{} approaches, but none were as readable. My error logs keep filling up with: "Found = in conditional, should be == at program.pl line 254." Any way to get this ignored (if it's not going to shoot me in the foot)?

Here's my sample:

$allowed{action} = 'post|edit|delete|long|short'; $query->{action} eq "post" ? ($allowed{msgid} = '^(\d+|new)$') && ( +$allowed{data} = '^(([^|+]*[^|]+.*\|){4}[^|+]*[^|]+.*)$'):1; $query->{action} eq "edit" ? $allowed{msgid} = '^(\d+|new)$':1; $query->{action} eq "delete" ? $allowed{msgid} = '^(\d+)$':1; $query->{action} eq "long" ? ($allowed{sort} = '^(author|date|subj +ect|expire)$') && ($allowed{msgid} = '^(\d*|all)$'):1; $query->{action} eq "short" ? ($allowed{sort} = '^(author|date|subj +ect|expire)$') && ($allowed{msgid} = '^(\d+|all)$'):1; foreach $key (keys %$query) { exists $allowed{$key} ? 1 : return 0; return 0 if $query->{$key}!~/$allowed{$key}/; 1; }

BTW, I iterate over the $allowed hash to

Replies are listed 'Best First'.
RE: How to write a nice rule base?
by reptile (Monk) on Aug 30, 2000 at 02:57 UTC

    hmm... how about a suggestion on a different approach? You could put your rule base into a hash, with the 'action' possibilities as keys, storing a hashref with the 'sort' and 'msgid' etc. keys, like this (truncated) example:

    my %RULES = ( 'post' => { 'msgid' => '^(\d+|new)$', 'data' => '...', }, 'edit' => { 'msgid' => '^(\d+|new)$', }, ..., }; my $allowed = $RULES{ $query->{'action'} }; foreach $key (keys %$query) { exists $allowed->{'key'} ? 1 : return 0; ... }

    Or, since it seems pretty obvious there can be only one action type at a time, you could probably cascade the trinary operator, like so:

    for ($query->{'action'}) { $allowed = /post/ ? { ... } : /edit/ ? { ... } : /delete/ ? { ... } : die "that action isn't supported"; }

    or any variation you like there. The hash-based one is probably faster.

    local $_ = "0A72656B636148206C72655020726568746F6E41207473754A"; while(s/..$//) { print chr(hex($&)) }

RE: rule base (comma operator / 'switch' construct)
by Russ (Deacon) on Aug 30, 2000 at 02:47 UTC
    The "Found = in Conditional" warning was not from the tertiary, but the logical and. Since you are not really doing a logical test, but just combining statements, I would use the comma operator, here.
    $allowed{action} = 'post|edit|delete|long|short'; $query->{action} eq "post" ? (($allowed{msgid} = '^(\d+|new)$'), ($allowed{data} = '^(([^|+]*[^|]+.*\|){4}[^|+]*[^|]+.*)$')) : 1; $query->{action} eq "edit" ? ($allowed{msgid} = '^(\d+|new)$') : 1; $query->{action} eq "delete" ? ($allowed{msgid} = '^(\d+)$') : 1; $query->{action} eq "long" ? (($allowed{sort} = '^(author|date|subject|expire)$'), ($allowed{msgid} = '^(\d*|all)$')) : 1; $query->{action} eq "short" ? (($allowed{sort} = '^(author|date|subject|expire)$'), ($allowed{msgid} = '^(\d+|all)$')) : 1; foreach $key (keys %$query){ exists $allowed{$key} ? 1 : return 0; return 0 if $query->{$key}!~/$allowed{$key}/; 1; }

    Now, that said, here's another way to handle this:

    # Map keys to coderefs (used sort of like a switch statement) my %ActionMap = (post => sub{ $allowed{msgid} = '^(\d+|new)$'; $allowed{data} = '^(([^|+]*[^|]+.*\|){4}[^|+]*[^|]+.*)$'; }, edit => sub{ $allowed{msgid} = '^(\d+|new)$'; }, delete => sub{ $allowed{msgid} = '^(\d+)$'; }, long => sub{ $allowed{sort} = '^(author|date|subject|expire)$'; $allowed{msgid} = '^(\d*|all)$'; }, short => sub{ $allowed{sort} = '^(author|date|subject|expire)$'; $allowed{msgid} = '^(\d+|all)$'; }); # "Call" the coderef matching the "switch" value $ActionMap{$query->{action}}->(); for my $key (keys %$query){ return 0 unless exists $allowed{$key} && $query->{$key} =~ /$allowed +{$key}/; }
    Notes:
    • I'm concerned about the for loop. It looks like $query->{$key} will never match /$allowed{$key}/. This was just a code example, though, so...
    • This "switch" construct avoids your void-context values. The ternary may feel easier to read, but it leaves the reader wondering what you are doing with that 1 in each ternary.
    • There are, of course, other ways to do "switch" statements...this is merely one of them.

    Russ
    Brainbench 'Most Valuable Professional' for Perl

RE: How to write a nice rule base?
by adamsj (Hermit) on Aug 30, 2000 at 02:26 UTC
    Instead of:
    $query->{action} eq "post" ? ($allowed{msgid} = '^(\d+|new)$') && ($al +lowed{data} = '^(([^|+]*[^|]+.*\|){4}[^|+]*[^|]+.*)$'):1;
    try this:
    $query->{action} eq "post" ? %allowed = (msgid => '(^\d+|new)$' , data + => '^(([^|+]*[^|]+.*\|){4}[^|+]*[^|]+.*)$)', action => 'post|edit|d +elete|long|short' ):1;
    and so on. That works for me, though there may be a better solution. Your message was cut off before you told us why you iterate over %allowed--if you don't need the value of action in the iteration, this can be made simpler. Also, consider doing something about your use of sort as a key--the -w flag doesn't like that, either.