I thought that %of
would be more readable and concise than percent-of
for a function name. Here is working code using the longer name.
#!/bin/env perl6
# Quick stats from gene_exp.diff file
sub percent-of
{
return sprintf('%.1f', (100 * $^a/ $^b).round(0.1));
}
my $total = first-word-from("wc -l gene_exp.diff ") -1; # subtract one for the header
my $ok = first-word-from "grep -c OK gene_exp.diff";
my $yes = first-word-from "grep -c yes gene_exp.diff";
put '| total | OK | OK % | yes | yes % | yes / OK |';
put "| $total | $ok | { percent-of $ok, $total } | $yes | { percent-of $yes,$total } | { percent-of $yes, $ok } |";
sub first-word-from ( $command )
{
return ( qqx{ $command } ).words[0];
}
Since I'm putting the subroutine name before its arguments, I would think that this would be a prefix operator. So here is what I thought would work to make the shorter name work (i.e. using sub prefix:<%of>
for declaring the function):
#!/bin/env perl6
# Quick stats from gene_exp.diff file
sub prefix:<%of>
{
return sprintf('%.1f', (100 * $^a/ $^b).round(0.1));
}
my $total = first-word-from("wc -l gene_exp.diff ") -1; # subtract one for the header
my $ok = first-word-from "grep -c OK gene_exp.diff";
my $yes = first-word-from "grep -c yes gene_exp.diff";
put '| total | OK | OK % | yes | yes % | yes / OK |';
put "| $total | $ok | { %of($ok, $total) } | $yes | { %of($yes,$total) } | { %of($yes,$ok) } |";
sub first-word-from ( $command )
{
return ( qqx{ $command } ).words[0];
}
But I get the following error:
| total | OK | OK % | yes | yes % | yes / OK |
Too few positionals passed; expected 2 arguments but got 1
in sub prefix:<%of> at /home/bottomsc/bin/gene_exp_stats line 6
in block <unit> at /home/bottomsc/bin/gene_exp_stats line 15
I'm sure something like what I'm attempting is possible. I've seen much crazier functions than this, like the infix I don't care operator ¯\(°_o)/¯
. What am I doing wrong? I get the exact same error when trying calling %of
with and without parentheses, so that isn't the issue.
As I was typing up this question, I just realized that I should try following the example just cited and doing this as an infix operator and it worked. However, I'm still curious as to why my prefix operator code didn't work. It's probably something very basic that I'm overlooking.
UPDATE:
Here is the working code when done as an infix operator. But I'm still curious about what I was doing wrong with the prefix version:
#!/bin/env perl6
# Quick stats from gene_exp.diff file
sub infix:<%of>
{
return sprintf('%.1f', (100 * $^a/ $^b).round(0.1));
}
my $total = first-word-from("wc -l gene_exp.diff ") -1; # subtract one for the header
my $ok = first-word-from "grep -c OK gene_exp.diff";
my $yes = first-word-from "grep -c yes gene_exp.diff";
put '| total | OK | OK % | yes | yes % | yes / OK |';
put "| $total | $ok | { $ok %of $total } | $yes | { $yes %of $total } | { $yes %of $ok } |";
sub first-word-from ( $command )
{
return ( qqx{ $command } ).words[0];
}
I thought that %of would be more readable and concise than percent-of for a function name.
Strongly disagree given this looks exactly like a hash sigil. If I was working with you I would go nuts if I ever came across this. What's wrong with percent-of
as an infix? 5 percent-of 100
returning 0.05 or something?
I read %of as hash called of. Never in a million years when reading Perl will I think % as a newly defined operator meaning 'percent' prefixed like that. So it's not readable given the language. It being concise isn't really a great justification either, it has much greater cognitive load so is again less readable. I'm not entirely sure what the balance to that criticism is? Other than it's frightening and cool you can do it at all...
An opening parenthesis is only interpreted as a function call when it follows a function name, not when it follows a prefix operator name.
So in your second code snippet, this expression does not call the %of
operator with two arguments:
%of($ok, $total)
Instead, it creates a List
value with two elements, and then applies the %of
operator to that single List
value - hence the error message "expected 2 arguments but got 1"
.
In order to pass multiple arguments to a prefix operator, you have to invoke its fully qualified name:
prefix:<%of>($ok, $total);
Of course this negates your goal of "readable and concise".
If you really want to use a prefix operator for this, you can make it accept a single List
parameter, and unpack it into two values right in the signature, by using a [ ]
subsignature:
sub prefix:<%of> ([$a, $b]) {
sprintf '%.1f', (100 * $a / $b).round(0.1);
}
say %of(42, 50); # 84.0
Note that declaring an operator that looks like a hash variable will likely confuse the next person who has to maintain your code (maybe even yourself, when you come back to it in a few weeks).
The built-in prefix operators like ++
or ~
have restricted themselves to one or two non-letter symbols for good reason. Perl 6 doesn't place the same restriction on user-defined operators, but remember that with great power comes great responsibility... :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With