Reputation: 2464
I am trying to solve a code golfing exercise which involves taking input the number of test cases, followed by an integer in each line for each test case.
In my quest to get the shortest possible solution I came across this solution:
#!perl -lp
use bigint;$_=???<>+1:bnok{2*$_}$_
I am very new perl, hence I am not getting how this code is actually working. I will be grateful if somebody help me figure this out.
PS: Asked and closed at codegolf.stackexhange.
Upvotes: 3
Views: 235
Reputation: 164769
There's a few tools for figuring out Perl golf. One is perlsecret which documents the "secret operators" and tricks used by many Perl golfers.
For this particular code, it's also important to understand what -p
does. It wraps the code in a bunch of other code which can then be exploited for golfing purposes.
Finally, the ultimate tool for unpacking Perl golf is B::Deparse. This translates how Perl understood the code back into human readable Perl with indentation and the like. The -p option makes B::Deparse add parenthesis even when they're not necessary which can help clarify precedence. The -d option gives a more accurate representation of objects. What objects? Observe.
$ perl -MO=Deparse,-p,-d
#!perl -lp
use bigint;$_=???<>+1:bnok{2*$_}$_
Use of ?PATTERN? without explicit operator is deprecated at - line 2.
BEGIN { $/ = "\n"; $\ = "\n"; }
use bigint;
LINE: while (defined(($_ = <ARGV>))) {
chomp($_);
BEGIN {
$^H{'bigint'} = '1';
$^H{'binary'} = 'CODE(0x7fc88ba3d580)';
$^H{'float'} = 'CODE(0x7fc88ba3d478)';
$^H{'integer'} = 'CODE(0x7fc88b298428)';
}
($_ = (?? ? (<ARGV> + bless( {"sign" => "+","value" => [1]}, 'Math::BigInt' )) : (bnok {
(bless( {"sign" => "+","value" => [2]}, 'Math::BigInt' ) * $_)
} $_)));
}
continue {
(print($_) or die("-p destination: $!\n"));
}
- syntax OK
A few of the more esoteric things this reveals...
?PATTERN?
operator plus the ?:
ternary operator. Specifically, ??
is the condition for the ternary.??
works just like //
but it will only match once until reset
is called. Because reset is never called in this program, it will only match once for this program. //
will match on anything.use bigint
turns all integers into Math::BigInt objects.bnok
is the binomial coefficient method from Math::BigInt.bnok
is being used as an indirect method call on the integers (which are really Math::BigInt objects). Indirect method calls are of the form method $object @args
so bnok{2*$_}$_
is actually (2*$_)->bnok($_)
which is the central binomial coefficient.The meat of the program can be understood a bit easier with some indentation and vertical whitespace. An editor with good paren matching is all you need to work them out.
(
$_ =
(?? ? (<ARGV> + bless( {"sign" => "+","value" => [1]}, 'Math::BigInt' ))
: (bnok {(bless( {"sign" => "+","value" => [2]}, 'Math::BigInt' ) * $_) } $_)
)
);
??
will only match once for the whole program, it's used to skip the first line which is the number of tests.??
will be false which will trigger the bnok
calculation.There is one detail I have not worked out. Why is it <>+1
and not <>
? It seems to suppress a blank line but I don't know why.
Upvotes: 7