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.
turns something like this:my(%s,$y,$l,$t,$d);
into this:my $k = k(\%s);
Limbic~Region reminds me that:$k = k();
... is still shorter, and if sub k is defined before it's called, it can be shorter still:$k=&k;
and this:$k=k;
into this:my $l = $d / int(length($t) / $k) / 100;
liverpole suggests that clever rearrangement of operands yields a few more savings:$l = $d / int(length($t) / $k) / 100;
Also, any numerical value assigned to $* is implicitly an int, and whio suggests that y///c is a character short than length, so now we have:$l = $d / 100 / int length($t)/ $k;
Note: Yes, I did drop dividing by 100. My original code wouldn't work without it, my new golfed code won't work with it. I don't know the exact change that removed its necessity.$* = $d / $t=~y///c / $k;
sub i{ my ($g,$l,$t) = @_; my @c; ... }
sub i{ my ($g,@c) = @_; ... }
becomes:for(3..6){u($_)}
u($_)for 3..6;
into this:while(<STDIN>){$t.=lc$_}
If you need to rotate an array, try it in a subroutine so you can take advantage of @_:$t.=lc for<>;
If it's an array of arrays that you want to rotate:push @_, shift;
push@$_, shift@$_ for @AoA;
into:my $n = t($_,$k); my %n = f($n);
Define sub t and sub f ahead of time, and you're left with:%n = f(t($_));
%n = f t$_;
and turns it into this:for(0..($k-1)){ my $n = t($_,$k); my %n = f($n); my @g = b(1,\%n); $y .= i(\@g,$l,\%t) }
map{ %n = f t$_; @g = b\%n,1; $y .= i\@g } 0..$k-1;
becomes:@array = routine1($param1,$param2); $result = join ':', @array;
Combining this idea with using map in a void context, inline functions, taking two suggestions from whio to eliminate an extra character in a comparison (turning >= into <), and the reminder that $x =~ /./g is shorter than split //, $x, and rearranging a few operands takes this code:$result = join ':', @{routine1($param1,$param2)}
and turns it into: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 }
map{ $p = index((join '', @{($y=~/./g)[$c]} ), $_); $o .= $p < 0 ? $_ : $a[$p]; $c += $c < $k-1 ? 1 : 1-$k } $t =~ /./gs;
becomes:for(a..z){ if( ! $d{$_} ){ $d{$_} = 0 } }
# no, you don't need a space between the 0 and the for. $d{$_} ||= 0for a..z;
with dropping the intermediary variable @k, becomes:my @k = map { $_->[1] } sort { $b->[0] <=> $a->[0] } @c; my $r = $k[0]; $r =~ s/([a-z])\s\(.*/$1/; return $r
return( map{ $_->[1] } sort { $b->[0] <=> $a->[0] } @c )[0]
More examples:
becomes:sub b{ my( $e, $l ) = @_; my @g; for(sort keys %$l){ push @g, [ $_, '=', (split //,'#' x int($$l{$_} * $e))] } return @g }
sub b { my( $e, $l, @g) = @_; push @g, [ $_, (split //,'#' x int($$l{$_} * $e))] for sort keys %$l +; @g }
becomes:sub f{ my %d; $d{$_}++ for grep /[a-z]/, split //, shift; for(a..z){ if( ! $d{$_}){ $d{$_} = 0 } } return %d }
sub f{ my %d; $d{$_}++for grep /[a-z]/, shift=~/./g; $d{$_} ||= 0for a..z; %d }
becomes: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 }
sub i{ my ($g,@c) = @_; map{ push @c, o(v($g), $$g[0][0]); w($g) } 0..25; ( map{ $_->[1] } sort { $b->[0] <=> $a->[0] } @c )[0] }
becomes: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) }
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 } c@g }
becomes:sub o{ my ($g,$w) = @_; my $c = 0; for( @$g ){ for( @$_ ){ /\+/ && $c++; /\-/ && $c-- } } return [$c,$w] }
sub o{ my ($g,$w,$c) = @_; map{ map{ /\+/ && $c++; /\-/ && $c--} @$_ } @$g; [$c,$w] }
becomes: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 }
sub t{ my ($o) = @_; my $c = 0; my $r; map{ $r .= $_ unless($k-$o+$c) % $k; $c++ } $t=~/./gs; $r =~ s/[^a-z]//g; $r }
becomes: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 }
sub v{ $m = pop; my @g = b\%t,$*; $s = \@g; $z = 0; map{ $x=0; map{ $$s[$z][$x] = $$m[$z][$x] eq '#' && $$s[$z][$x] eq '#' ? '+ +' : '-'; $x++ } @$_; $z++ } @$m; $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!
Updated: incorporated some suggestions from Limbic~Region, liverpole, and whio.
$,=42;for(34,0,-3,9,-11,11,-17,7,-5){$*.=pack'c'=>$,+=$_}for(reverse s +plit//=>$* ){$%++?$ %%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[$%++]}
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Perl Golf 101
by whio (Beadle) on May 27, 2006 at 01:32 UTC | |
by chargrill (Parson) on May 27, 2006 at 06:45 UTC | |
by whio (Beadle) on May 27, 2006 at 07:36 UTC | |
Re: Perl Golf 101
by liverpole (Monsignor) on May 26, 2006 at 15:01 UTC | |
by chargrill (Parson) on May 26, 2006 at 16:00 UTC | |
by sgt (Deacon) on May 30, 2006 at 22:17 UTC | |
by chargrill (Parson) on May 31, 2006 at 00:10 UTC | |
Re: Perl Golf 101
by blazar (Canon) on May 26, 2006 at 14:57 UTC | |
by szbalint (Friar) on May 26, 2006 at 18:15 UTC | |
by chargrill (Parson) on May 26, 2006 at 15:57 UTC | |
by nimdokk (Vicar) on May 26, 2006 at 16:05 UTC | |
by jwkrahn (Abbot) on May 27, 2006 at 09:26 UTC | |
by locked_user mtve (Deacon) on May 28, 2006 at 14:02 UTC |