I was recently trying to golf down some code to post as an obfuscation, and in a flash of inspiration happened upon some golfing techniques that I've never been able to find listed in a tidy package, so I thought I'd post my tips and tricks (relatively basic though they are) for anyone on a similar quest to shave characters.
This isn't meant as an exhaustive treatise on golfing, there are others much better at it than I. However, I was unable to find a good "howto" anywhere, and though I'd make a little one here. It's my hope that others better at this sport might share some tidbits of their own.
Note: It's my hope to polish this up and have it moved to the tutorial section. I'll leave this as a readmore until then.
Important: NO tip or technique presented here has ANY reason to show up in production code - golf is fine for fun and games, but can and will cause production code to fail to run properly, fail to be easy to maintain, etc.
my(%s,$y,$l,$t,$d);turns something like this:
my $k = k(\%s);into this:
$k = k();and this:
my $l = $d / int(length($t) / $k) / 100;into this:
$l = $d / int(length($t) / $k) / 100;Hint: $_ doesn't need to be declared as a my variable.
sub i{
my ($g,$l,$t) = @_;
my @c;
...
}
sub i{
my ($g,@c) = @_;
...
}
for(3..6){u($_)}
becomes:
u($_)for 3..6;
while(into this:){$t.=lc$_}
$t.=lc for<>;If you need to rotate an array, try it in a subroutine so you can take advantage of @_:
push @_, shift;If it's an array of arrays that you want to rotate:
push@$_, shift@$_ for @AoA;
my $n = t($_,$k); my %n = f($n);into:
%n = f(t($_));
for(0..($k-1)){
my $n = t($_,$k);
my %n = f($n);
my @g = b(1,\%n);
$y .= i(\@g,$l,\%t)
}
and turns it into this:
map{
%n = f(t($_));
@g = b(1,\%n);
$y .= i(\@g)
} 0..$k-1;
@array = routine1($param1,$param2); $result = join ':', @array;becomes:
$result = join ':', @{routine1($param1,$param2)}
Combining this idea with using map in a void context, inline functions takes this code:
for((split//,$t)){
my $l = (split //,$y)[$c];
my $s = join '', @$l;
my $p = index($s,$_);
$o .= $p >= 0 ? $a[$p] : $_;
$c += $c < $k-1 ? 1 : -$k+1
}
and turns it into:
map{
$p = index((join '', @{(split //,$y)[$c]} ), $_);
$o .= $p >= 0 ? $a[$p] : $_;
$c += $c < $k-1 ? 1 : 1-$k
} split //, $t;
for(a..z){
if( ! $d{$_} ){
$d{$_} = 0
}
}
becomes:
$d{$_} ||= 0 for a..z;
my @k = map { $_->[1] } sort { $b->[0] <=> $a->[0] } @c;
my $r = $k[0];
$r =~ s/([a-z])\s\(.*/$1/;
return $r
with dropping the intermediary variable @k, becomes:
return( map{ $_->[1] } sort { $b->[0] <=> $a->[0] } @c )[0]
More examples:
sub b{
my( $e, $l ) = @_;
my @g;
for(sort keys %$l){
push @g, [ $_, '=', (split //,'#' x int($$l{$_} * $e))]
}
return @g
}
becomes:
sub b {
my( $e, $l, @g) = @_;
push @g, [ $_, (split //,'#' x int($$l{$_} * $e))] for sort keys %$l;
return @g
}
sub f{
my %d;
$d{$_}++ for grep /[a-z]/, split //, shift;
for(a..z){
if( ! $d{$_}){
$d{$_} = 0
}
}
return %d
}
becomes:
sub f{
my %d;
$d{$_}++ for grep /[a-z]/, split //, shift;
$d{$_} ||= 0 for a..z;
return %d
}
sub i{
my ($g,$l,$t) = @_;
my @c;
for(0..25){
my $v = v($g,$l,$t);
push @c, o($v,$$g[0][0]);
w($g)
}
my @k = map{ $_->[1] }
sort{ $b->[0]<=>$a->[0] } @c;
my $r = $k[0];
$r =~ s/([a-z])\s\(.*/$1/;
return $r
}
becomes:
sub i{
my ($g,@c) = @_;
map{ push @c, o(v($g), $$g[0][0]); w($g) } 0..25;
return( map{ $_->[1] } sort { $b->[0] <=> $a->[0] } @c )[0]
}
sub k{
my $s = shift;
my @g;
for( sort{ $$s{$b} <=> $$s{$a} || $a cmp $b } keys %$s ){
last if $$s{$_} < 3;
next unless $_ =~ y/a-z// > 2;
my @f;
push @f, ( pos($t)-3 ) while $t =~ /$_/g;
my $g = c(n(@f));
$g > 2 && push @g, $g
}
return c(@g)
}
becomes:
sub k{
my @g;
for( sort{ $s{$b} <=> $s{$a} } keys %s ){
last if $s{$_} < 3;
next unless y/a-z// > 2;
my @f;
push @f, (pos($t)-3) while $t =~ /$_/g;
my $g = c(n(@f));
$g > 2 && push @g, $g
}
return c(@g)
}
sub o{
my ($g,$w) = @_;
my $c = 0;
for( @$g ){
for( @$_ ){
/\+/ && $c++;
/\-/ && $c--
}
}
return [$c,$w]
}
becomes:
sub o{
my ($g,$w) = @_;
my $c = 0;
map{
map{ /\+/ && $c++; /\-/ && $c--} @$_
} @$g;
return [$c,$w]
}
sub t{
my ($o,$k) = @_;
my $c = 0;
my $r;
for(split //,$t){
$r .= $_ unless(($c+($k-$o)) % $k);
$c++
}
$r =~ s/[^a-z]//g;
return $r
}
becomes:
sub t{
my ($o) = @_;
my $c = 0;
my $r;
map{ $r .= $_ unless($k-$o+$c) % $k; $c++ } split //,$t;
$r =~ s/[^a-z]//g;
return $r
}
sub v {
my ($m,$l,$t) = @_;
my @g = b($l,$t);
my $s = \@g;
my $z = 0;
for( @$m ){
my $x = 0;
for( @$_ ){
if( $$m[$z][$x] eq '#' && $$s[$z][$x] eq '#' ){
$$s[$z][$x] = '+'
}
elsif( $$m[$z][$x] eq '#'&&$$s[$z][$x] ne '#' ){
$$s[$z][$x] = '-'
}
$x++
}
$z++
}
return $s
}
becomes:
sub v{
my ($m) = @_;
my @g = b($l,\%t);
$s = \@g;
$z = 0;
map{ $x=0;
map{
$$s[$z][$x] = $$m[$z][$x] eq '#' && $$s[$z][$x] eq '#' ? '+' : '-';
$x++
} @$_;
$z++
} @$m;
return$s
}
Well, those are all the tips and tricks I have used in a recent obfuscation, where I shaved about 200 characters off of 1785 or so. Could it have been golfed further? Probably. Let me know if there's anything I've missed!
$,=42;for(34,0,-3,9,-11,11,-17,7,-5){$*.=pack'c'=>$,+=$_}for(reverse split//=>$*
){$%++?$ %%2?push@C,$_,$":push@c,$_,$":(push@C,$_,$")&&push@c,$"}$C[$#C]=$/;($#C
>$#c)?($ c=\@C)&&($ C=\@c):($ c=\@c)&&($C=\@C);$%=$|;for(@$c){print$_^$$C[$%++]}
This isn't meant as an exhaustive treatise on golfing, there are others much better at it than I. However, I was unable to find a good "howto" anywhere, and though I'd make a little one here. It's my hope that others better at this sport might share some tidbits of their own.
There used to be a nice and very extensive reference at http://terje2.perlgolf.org/~golf-info/Book.html, but the domain seem to have expired. I should have a local copy at home.
Yes, in fact I specifically recall that exact URL when I was looking for hints and tips, but couldn't even convince google to spit out a cache. If you could post your local copy somewhere, that would be awesome, as a relative newcomer to golf, I know I'm already starting at a handicap. :)
$,=42;for(34,0,-3,9,-11,11,-17,7,-5){$*.=pack'c'=>$,+=$_}for(reverse split//=>$*
){$%++?$ %%2?push@C,$_,$":push@c,$_,$":(push@C,$_,$")&&push@c,$"}$C[$#C]=$/;($#C
>$#c)?($ c=\@C)&&($ C=\@c):($ c=\@c)&&($C=\@C);$%=$|;for(@$c){print$_^$$C[$%++]}
< mtve> we with km forgot to pay for it in time, then it was freed and now it's taken away by those mo***s.
From a conversation at #perlgolf, talking about perlgolf.org yesterday.
One note, don't forget that the return of a subroutine is the last statement of the subroutine. So instead of:
sub o{
my ($g,$w) = @_;
my $c = 0;
map{
map{ /\+/ && $c++; /\-/ && $c--} @$_
} @$g;
return [$c,$w]
}
You can just do:
sub o{
my ($g,$w) = @_;
my $c = 0;
map{
map{ /\+/ && $c++; /\-/ && $c--} @$_
} @$g;
[$c,$w]
}
You know, the funny thing is that I know that, and that was one of the first changes I made in going from my full, fairly properly coded version to the golfed version. However, I specifically recall that there was at least one of my subs that refused to function correctly after the change. Was it from removing the explicit return, or some other change I made? I don't know, but I do know that restoring the return (and probably some other subtle changes) restored the functionality, so from that point on, dropping the explicit return was left out of my golf bag.
$,=42;for(34,0,-3,9,-11,11,-17,7,-5){$*.=pack'c'=>$,+=$_}for(reverse split//=>$*
){$%++?$ %%2?push@C,$_,$":push@c,$_,$":(push@C,$_,$")&&push@c,$"}$C[$#C]=$/;($#C
>$#c)?($ c=\@C)&&($ C=\@c):($ c=\@c)&&($C=\@C);$%=$|;for(@$c){print$_^$$C[$%++]}
actually a few months ago I remember a discussion on p5p on return. with no explicit return and with control structures it's actually kind of a lottery to guess the actual *return*
the sad thing was that it was no easy to fix... and if I recall correctly PBP says always use return ...orthogonal to golf
...orthogonal to golf
Indeed - I dare anyone to run perlcritic* against any golfed code :-) (Which is why I posted the big notice about not letting any of these "techniques" make it into production code ;)
I've gotten in the habit of running perlcritic against pretty much all of my production level code these days, and taking the output with a grain of salt. My .perlcriticrc does contain quite a few things from PBP that I don't particularly agree with...
* For those unfamiliar with it, perlcritic is a commandline utility that (by default) critiques your code against a set of rules as outlined by Perl Best Practices.
$,=42;for(34,0,-3,9,-11,11,-17,7,-5){$*.=pack'c'=>$,+=$_}for(reverse split//=>$*
){$%++?$ %%2?push@C,$_,$":push@c,$_,$":(push@C,$_,$")&&push@c,$"}$C[$#C]=$/;($#C
>$#c)?($ c=\@C)&&($ C=\@c):($ c=\@c)&&($C=\@C);$%=$|;for(@$c){print$_^$$C[$%++]}
From the snippets you have posted:
$o .= $p >= 0 ? $a[$p] : $_;could become $o .= $p < 0 ? $_ : $a[$p] and
$r =~ s/[^a-z]//g;could become $r =~ y/a-z//cd;
Excellent tips! I'll have to incorporate those into my golfed obfu, though I shan't post an updated version because its format is quite well fixed with the existing number of characters it had prior to the application of some of the tips received since posting. Since the first round of feedback from this node, I've already cut down another ~60 characters, and with yours I'm sure I'll cut down quite a few as well, as the original makes a fair amount of use of some of the idioms you're suggesting which can be golfed further.
I sort of hinted at postfixing for not requiring the parens of braces without stating it explicitly, along with order of precedence.
As far as builtin variables, while I know what $- and $= are (and default to), I don't have anything in my notes about special effects they have - just a note for $* making any numerical value assigned to it an implicit int (which gives me an idea... ;-)
I will more than likely incorporate some of your suggestions (and especially your explanations!) into my original post, with of course your permission (and proper attribution on my part).
$,=42;for(34,0,-3,9,-11,11,-17,7,-5){$*.=pack'c'=>$,+=$_}for(reverse split//=>$*
){$%++?$ %%2?push@C,$_,$":push@c,$_,$":(push@C,$_,$")&&push@c,$"}$C[$#C]=$/;($#C
>$#c)?($ c=\@C)&&($ C=\@c):($ c=\@c)&&($C=\@C);$%=$|;for(@$c){print$_^$$C[$%++]}
perlmonks.org content © perlmonks.org and blazar, chargrill, jwkrahn, liverpole, mtve, nimdokk, sgt, szbalint, whio
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03