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

First of all, sorry for this provocative question. Please no flames.

The question is the optimizing of one Perl program, which is an interpreter of an esoteric programming language called ETA

Shortest Perl variant that I can imagine is 400 bytes:

map{push@L,@C+0;push@C,split//,lc}<>;$n=0;while($n<@C){$_=$C[$n++];if( +/n/){$0=0; /-/ or$0=$_+$0*7while($_=index"htaoinse",$C[$n++])<7;push@n,$0}if(/t/) +{$_=pop@n; $_?$n=$L[$_-1]:last if pop@n}/a/&&push@n,1+grep$n>$_,@L;if(/h/){$_=pop +@n;push@n, $n[$#n-abs];$_>0&&splice@n,@n-2-$_,1}/o/&&print+chr(pop@n);/s/&&push@n +,-(pop@n)+ pop@n;$0=pop@n,$_=pop@n,push@n,int$_/$0,$_%$0if/e/;push@n,($_=getc)?or +d:-1if/i/}

Ruby version by Stephen Sykes is 357 bytes:

n=[];p=$<.readlines;f=l=c=0;loop{p[l].downcase.each_byte{|y|f>0?$6?(n< +<c;c=f=0): c="htaoins".index(y)+c*7: $1?n<<l+2: $2?n<<STDIN.getc: $3?f=1:(a=n.pop +;$4?$><<a. chr: $5?(n<<n[n.size-a.abs-1];a>0?n.delete_at(n.size-a-2):1):(b=n.pop; +$6?(n<<b/a ;n<<b%a): $7?n<<b-a: b!=0?(l=a-2;break):1))if y.chr=~"(a)|(i)|(n)|(o)| +(h)|(e)|(\ s)|(t)"};break if(l+=1)>=p.size||l<0} #Ruby ETA by S. +Sykes 2002

Please help to improve Perl version.

You can use any 'standard' obfuscated Perl tricks, change algorithm etc.

Your program must be able to run Tic-Tac-Toe program, feet in 80 columns and don't produce unnecessary warnings on stable 5.6.1

Extra bonus points for bilingual results, i.e. if your Perl program will not crash as ETA on reference interpreter

Replies are listed 'Best First'.
Re: Is Perl less compact than Ruby? (Kind of competition)
by cLive ;-) (Prior) on Feb 18, 2002 at 21:12 UTC
    Well, since it doesn't work under strict, you can safely drop:
    $n=0;
    that's another 5 chars.

    Also, put pop@n in a sub at the beginning:

    sub p{pop@n}
    then replace:
    pop@n => p
    throughout. another 20 chars saved.
    sub w{push@n,$_[0]}sub p{pop@n}map{push@L,@C+0;push@C,split//,lc}<>;wh +ile($n<@C){$_=$C[$n++];if(/n/){$0=0;/-/ or$0=$_+$0*7while($_=index"ht +aoinse",$C[$n++])<7;w$0}if(/t/){$_=p@n;$_?$n=$L[$_-1]:last if p@n}/a/ +&&push@n,1+grep$n>$_,@L;if(/h/){$_=p@n;$n[$#n-abs];$_>0&&splice@n,@n- +2-$_,1}/o/&&print+chr(p@n);/s/&w-(p@n)+p@n;$0=p@n,$_=p@n,w(int$_/$0), +$_%$0if/e/;w($_=getc)?ord:-1if/i/}

    cLive ;-)

    Update - d'oh, yep, another 16 chars saved by changing p@n to p (updated above) - that means we can also save another 10 chars by whacking push@n,'whatever' into a sub.

    What's that now? 379 chars?

    --
    seek(JOB,$$LA,0);

      Excellent!

      pop@n => p@n

      or simply to 'p', yes?

        and another similar optimization on push, now EQUAL TO RUBY - 357 bytes!!!

        sub p{pop@n}sub v{push@n,@_}map{push@L,@C+0;push@C,lc=~/./g}<>;while($ +n<@C){$_= $C[$n++];if(/n/){$0=0;/-/ or$0=$_+$0*7while($_=index"htaoinse",$C[$n++ +])<7;v$0} if(/t/){$_=p;$_?$n=$L[$_-1]:last if p}/a/&&v 1+grep$n>$_,@L;if(/h/){$_ +=p;v$n[ $#n-abs];$_>0&&splice@n,@n-2-$_,1}/o/&&print+chr(p);/s/&&v-(p)+p;$0=p, +$_=p,v int$_/$0,$_%$0if/e/;v(($_=getc)?ord:-1)if/i/}

        Thank you!!! What's next?

Re: Is Perl less compact than Ruby? (Kind of competition)
by tadman (Prior) on Feb 18, 2002 at 19:27 UTC
    Before pulling apart the interpreter, you can at least shave a character off the preamble using the old m// trick:
    # Formerly: split//,lc # Revised: lc=~m/./g # Revised revision [mtve]: lc=~/./g
    Chalk one up for Perl!

    Update: mtve points out the obvious. D'oh!
      Thanks! Worked even without m, is it ok?
Re: Is Perl less compact than Ruby? (Kind of competition)
by grinder (Bishop) on Feb 18, 2002 at 22:40 UTC

    By folding the suggestions encountered so far, and adding in some of my own, I was able to bring the total down to 342 characters. Big gains can be made by observing that $foo[-2] refers to indices at the end of an array. That happens twice in this script.

    In the part where the code pops to $_ and $0, it's cheaper to just call pop @n directly. This was the case with pop@n, and I guess it will still hold with p.

    sub w as written contains a bug. Sometimes you push more than one element on. But that's okay, @_ is a win over $_[0].

    I tried changing expr if cond to cond&&expr but that didn't do anything.

    map{push@L,@C+0;push@C,lc=~/./g}<>;sub p{pop@n}sub w{push@n,@_}while($ +n<@C){$_= $C[$n++];if(/n/){$0=0;/-/ or$0=$_+$0*7while($_=index"htaoinse",$C[$n++ +])<7;w$0} if(/t/){$_=p;$_?$n=$L[$_-1]:last if p}/a/&&w 1+grep{$n>$_}@L;if(/h/){$ +_=p;w$n[- abs$_];$_>0&&splice@n,-1-$_,1}/o/&&print chr p;/s/&&w-p()+p;/e/&&w int + p()/p,p% p;w($_=getc)?ord:-1if/i/}

    Note that changing /s/&&v-(p)+p to /s/&&v+p-p breaks the interpreter. Or at least, the hello.eta program no longer runs correctly, which was the extent of my testing. The interpreter is quite slow.


    print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'

      but a few bugs exists.

      p has side effect of modifying @n, so for example in /e/ part you must call it only twice not four times.

      in last expression correct logic is w+($_=getc)?ord:-1if/i/

Good and bad news
by locked_user mtve (Deacon) on Feb 19, 2002 at 08:04 UTC

    Good news is that by common efforts we clear first hurdle, 357 bytes.

    Bad news is that Ruby is also don't stand still, and as you can see here it's now four lines and 306 bytes long!

    n=[];p=$<.readlines;$==f=l=c=0;loop{p[l].gsub(/(a)|(i)|(n)|(o)|(h)|(e) +|(s)|t/){ f>0?$6?n<<c&&c=f=0:c="htaoins".index($&)+c*7: $1?n<<l+2: $2?n<<STDIN.g +etc: $3?f= 1:(a=n.pop;$4?$><<a.chr: $5?n<<n[-1-a.abs]&&a>0&&n.slice!(-2-a):(b=n.p +op;$6?n<< b/a&&n<<b%a: $7?n<<b-a: b!=0&&(l=a-2;break)))};p[l+=1]&&l>=0||break}

    How do you like this intrigue?

      is 342 bytes

      map{push@L,@C+0;push@C,lc=~/./g}<>;sub p{pop@n}sub w{push@n,@_}while($ +n<@C){$_= $C[$n++];if(/h/){$_=p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1}if(/n/){w 0;/ +-/||w$_+7 *p while($_=index"htaoinse",$C[$n++])<7}$0=p,$_=p,w int$_/$0,$_%$0if/e +/;if(/t/) {$_=p;$_?$n=$L[$_-1]:last if p}/a/?w 1+grep$n>$_,@L:/o/?print chr p:/s +/?w-(p)+p :/i/?w+($_=getc)?ord:-1:0}

      Beg you for another 26 bytes, is it possible?

        Down to 340 bytes:

        map{push@L,@C+0;push@C,lc=~/./g}<>;sub p{pop@n}sub w{push@n,@_}while($ +n<@C){$_= $C[$n++];if(/h/){$_=p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1}if(/n/){w 0;/ +-/||w$_+7 *p while($_=index"htaoinse",$C[$n++])<7}$0=p,$_=p,w int$_/$0,$_%$0if/e +/;if(/t/) {$_=p;$_?$n=$L[$_-1]:last if p}/a/?w 1+grep$n>$_,@L:/o/?print chr p:/s +/?w-(p)+p :/i/&&w($_=getc)?ord:-1}

        We can s/last if p/p&&last/ and that brings it own to 338 bytes:

        map{push@L,@C+0;push@C,lc=~/./g}<>;sub p{pop@n}sub w{push@n,@_}while($ +n<@C){$_= $C[$n++];if(/h/){$_=p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1}if(/n/){w 0;/ +-/||w$_+7 *p while($_=index"htaoinse",$C[$n++])<7}$0=p,$_=p,w int$_/$0,$_%$0if/e +/;if(/t/) {$_=p;$_?$n=$L[$_-1]:p&&last}/a/?w 1+grep$n>$_,@L:/o/?print chr p:/s/? +w-(p)+p: /i/&&w($_=getc)?ord:-1}

        But wait, there's more! change $0=p,$_=p,w int$_/$0,$_%$0 to w int($_=p)/($0=p),$_%$0} Which gives 337 (I think) bytes:

        map{push@L,@C+0;push@C,lc=~/./g}<>;sub p{pop@n}sub w{push@n,@_}while($ +n<@C){$_= $C[$n++];if(/h/){$_=p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1}if(/n/){w 0;/ +-/||w$_+7 *p while($_=index"htaoinse",$C[$n++])<7}w int($_=p)/($0=p),$_%$0if/e/; +if(/t/) {$_=p;$_?$n=$L[$_-1]:p&&last}/a/?w 1+grep$n>$_,@L:/o/?print chr p:/s/? +w-(p)+p: /i/&&w($_=getc)?ord:-1}

        A ternary operator can be replaced by a && and the plus in w+($_=getc)?ord:-1 trick is not needed, it works without (or, at least, hello.eta still outputs correctly).

        I have an idea it must be possible to replace the w$_+7*p while($_=index"htaoinse",$C[$n++])<7 section with a redo and/or a pos, but it is so difficult to understand what the program does I am beginning to wonder whether it wouldn't be easier to look at the instruction set and come up with another angle of attack. For instance, it would be nice to be able to do away with the @L array as well. Replace @C and @L by a hash that is keyed on byte offset and the value is the byte.


        print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
Re: Is Perl less compact than Ruby? (Kind of competition)
by dragonchild (Archbishop) on Feb 19, 2002 at 17:14 UTC
    339.
    map{push@L,@C+0;push@C,lc=~/./g}<>;sub p{$_=pop@n}sub w{push@n,@_}whil +e($n<@C){$_=$C[$n++];if(/h/){p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1}if( +/n/){w 0;$b<0||w$b+7*p while($b=index"htaoinse",$C[$n++])<7}$0=p,p,w +int$_/$0,$_%$0if/e/;if(/t/){$a=p;$a?$n=$L[$a-1]:last if p}/a/?w 1+gre +p$n>$_,@L:/o/?print chr p:/s/?w-(p)+p:/i/&&w+($_=getc)?ord:-1}
    The big change was sub p{$_=pop@n} and the subsequent fall-out. *pants*\

    Update: To 336...

    if(/t/){$a=r;$a?$n=$L[$a-1]:last if r} --- $a=r,r&&($a?$n=$L[$a-1]:last)if/t/;

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Another update, bringing to 331. Though I'm not sure the change is legal, it runs the Tic-Tac-Toe program.
      map{push@L,push@C,lc=~/./g}<>;sub p{$_=pop@n}sub w{push@n,@_}while($n< +@C){$_=$C[$n++];if(/h/){p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1}if(/n/){ +w 0;$b<0||w$b+7*p while($b=index"htaoinse",$C[$n++])<7}$0=p,p,w int$_ +/$0,$_%$0if/e/;$a=p,p&&($a?$n=$L[$a-2]:last)if/t/;/a/?w 2+grep$n>$_,@ +L:/o/?print chr p:/s/?w-(p)+p:/i/&&w+($_=getc)?ord:-1}
      The change was to functionally remove the 0 at the beginning of @L. Adjusting the subscripts used to access @L seems to result in an acceptable performance.

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        One thing people often forget is that push@L,values is a semantically equivalent (modulo being faster) to @L=@L,values - a saving of one character per push, or three characters in the above, bringing you down to 328.

        Excellent tweak with push!

        map{push@L,push@C,lc=~/./g}0,<>; should fix this. It's nice question but I believe impossibility to jump on first line is some violation of language. It's my fault of incorrect target setting.

      confirmed. i can't believe it! 336 bytes.

      only 16 bytes to make it four-liner.

Re: Is Perl less compact than Ruby? (Kind of competition)
by jynx (Priest) on Feb 19, 2002 at 19:03 UTC

    As for that last section,

    In my testing it seems to work with the following change (saving 4 more characters):

    /i/&&w+($_=getc)?ord:-1 # goes to: /i/&&w(ord getc|-1)
    jynx

    update: take out the parens on that up there:

    /i/&&w ord getc|-1
    for another 1 char savings...

      /i/&& w ord getc||-1
      You forgot a '|'. That brings us to 306. :-)

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.


        my testing has been limited,

        i've only been using hello.eta and hello2.eta, but using | seems to be equivalent to || there. i tested both ways and they seem to work okay on the two hello programs. did your tests come out differently? if so, which programs should i be testing with (that is, which are more indicative of a real ETA program?)

        As for the tic-tac-toe program, no matter which of the recent incarnations of the perl program i try, it seems to hang after i've given it input. does it just take a really long time or is it still running?

        jynx

Re: Is Perl less compact than Ruby? (Kind of competition)
by blakem (Monsignor) on Feb 18, 2002 at 19:35 UTC
    Oops... nothing to see here.

    While I don't have time to actually play this round of golf, the perl version can definately be shortened. For instance, the while clause in the first line jumps out as being longer than necessary:

    while($n<@C){$_=$C[$n++]};
    Becomes:
    $_=$C[$n++]while$n<@C;
    Shaving 4 chars off of the total.....

    -Blake

      It's longer loop.
Hooray!
by locked_user mtve (Deacon) on Feb 20, 2002 at 21:18 UTC

    I must say it again, WE WIN with 290 bytes:

    sub p{$_=pop@n}sub w{push@n,@_}$/=$T;$_=uc<>;s#.#+{qw(E 7$0=p;p;w+int$ +_/$0,$_%$0 H 0p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1 I 4w+($_=getc)?ord:-1 A 2w$L+1 + N 5w$N++ S 6w-(p)+p O 3print+chr(p) T 1$t=p;p&&goto"L$t")}->{$&}=~/./&&"\$N?$&> +6?\$N=0:". "w $&+7*p:do{$'};"#eg;s/^/L${\++$i}:\$L=$i;/mg;eval
Latest results
by locked_user mtve (Deacon) on Feb 20, 2002 at 11:26 UTC

    Thanks to tadman, cLive ;-) , blakem, dragonchild, grinder, Matts, jynx and PerlMonks.org, Perl version in now
    officially 333 bytes (or 327 bytes with some cheating), while Ruby is still 306 bytes.

    map{push@L,push@C,lc=~/./g}0,<>;sub p{$_=pop@n}sub w{push@n,@_}while($ +n<@C){$_=$ C[$n++];if(/h/){p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1}if(/n/){w 0;$b<0| +|w$b+7*p while($b=index"htaoinse",$C[$n++])<7}$0=p,p,w int$_/$0,$_%$0if/e/;$a=p +,p&&($a?$n =$L[$a-1]:last)if/t/;/a/?w 1+grep$n>$_,@L:/o/?print chr p:/s/?w-(p)+p: +/i/&&w+($_ =getc)?ord:-1}

      Beta version, 325 bytes, expanded view, subject to optimizations:

      sub p{$_=pop@n} sub w{push@n,@_} %D=( H=>'0p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1', T=>'1$t=p;p&&goto"L$t"', A=>'2w$L+1', O=>'3print chr p', I=>'4w+($_=getc)?ord:-1', N=>'5w$N++', S=>'6w-(p)+p', E=>'7$0=p;p;w int$_/$0,$_%$0' ); $/=\-1;$_=uc<>; s#.#$D{$&}=~/(.)(.*)$/&&"\$N?$1>6?\$N=0:w $1+7*p:do{$2};"#eg; s/^/$i++,"L$i:\$L=$i;"/emg; eval; L0:
        Here's some more, then. It also adds the (missing?) newline to the end of each command.
        sub p{$_=pop@n} sub w{push@n,@_} $/=$T;$_=uc<>; s#.#+{qw( H 0p;w$n[-1-abs];$_>0&&splice@n,-2-$_,1 T 1$t=p;p&&goto"L$t" A 2w$L+1 O 3print+chr+p I 4w+($_=getc)?ord:-1 N 5w$N++ S 6w-(p)+p E 7$0=p;p;w+int$_/$0,$_%$0 )}->{$&}=~/./&&"\$N?$&>6?\$N=0:w $&+7*p:do{$'};\n"#eg; s/^/L${\++$i}:\$L=$i;/mg; eval; L0:

        _____________________________________________________
        Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a (from-home) job
        s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

        Nicely done! Shave three with I=>'4w ord getc||-1',.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.