In article <[EMAIL PROTECTED]>, [EMAIL PROTECTED] writes: > Ton, if you can find the time, I would really appreciate a little > write-up on your unorthodoxy too. How come you knew about it, > why did you choose it instead of $. in your solution, and so on. > The thinking, heh ? Ok.
When i hear even/odd, I directly think about the last digit in a binary representation. You can use "&" between terms to demand every last digit is 1, and "|" between terms to demand every last digit is 0. For counting characters the "tr" operator is well known, and can be written even shorter as "y". You can make several variations to switch between even and odd results too: put "~" in front to toggle the bits, put "c" behind to count the other set of chars, put a "\n" inside the body to get one extra or add a "-l" on the commandline to get an automatic chop. Then there is the even/odd linenumber. Again there are many ways to do that. You can try to use the scalar ".." operator to flip-flop (everything I tried there was too long), use -aF.* and pick up the lines from @F (long !), read <> once more inside the loop (unfortunately that causes the loop to restart reading from STDIN if the number of lines is odd), and of course there is $. and the bit-flipped version ~$. And less well known, there is $|-- and its opposite --$|. I discovered these once by accident when i tried to always print a line flushed while keeping $| to it's old value and not using a block with local: $|++; print "whatever"; $|--; This turned out to NOT work, and while investigating why, i saw that $| can only be 0 or 1, which made me realise that -1 will become 1, and that you can use --$| as a flip-flop. $|-- and --$| have one big advantage over $. and ~$. which is that they are already 0 or 1 and to get the last digit of the binary expansion, at the end a "&" with 1 will have to happen. $| can directly play the role of this "1". (the "&" with 1 is also the reason that combining with "|" tends to be a bit long, since it needs () around the expression because "&" has higher priority). Another nice thing is that you can use them in something like "for<>", because they don't depend on the current linenumber (all reading is finished by the time the for body gets its turn). The next step was to combine it all into a program. Since y/// does not do variable interpolation, it will need an eval. And the string to be evalled can probably best be constructed instead of written since the target expression is quite long and very repetitive. Two obvious ways of constructing are s/// and array substitution in a string after setting $, (i never knew about the glob trick). So my first attempt was: -nl ($a=aeiouy)=~s!.*?!|y/$&//c&1|!g;eval"--\$|$a|print" The eval is applied on: --$||y///c&1||y/a//c&1||y///c&1||y/e//c&1||y///c&1||y/i//c&1||y///c&1||y/o//c&1||y///c&1||y/u//c&1||y///c&1||y/y//c&1||y///c&1||print obviously this is way too long. One of the reasons is that I had to write the $&, so i decided to instead try to put the y/// "inbetween" the characters instead of around. look at the output of this: perl -le '$_=aeiouy;s!!//&y/!g;print' //&y/a//&y/e//&y/i//&y/o//&y/u//&y/y//&y/ So now all the remains to be done is to somehow use the // you get at the start and the y/ at the end. and there are two missing jobs: skipping lines and counting the total length. Also i'm free to use lots of other chars than / if that happens to be useful for the thing i'm going to add. At this point it becomes a question of combining all variations mentioned above to get to a short solution. I found nothing shorter than these two: -ln ($a=aeiouy)=~s!!--&~y-!g;eval"\$|$a--c"&&print -n ($a=aeiouy)=~s!!//c&y/!g;eval"~$.&$a//c"&&print The first one is nice in that it uses the -- at the start to attach to the $| to get $|--, the second is nice in that it uses the //c at the start as the "1" with which we need to "&" anyways. Since they were equal length, i sent the first one, because I thought it looked weirder. Two things are pretty frustrating about this solution: 1) It doesn't use the "print" you can get for free with -p Things like "next" also don't work, since the -p loop has that annoying implied "continue". The best I could think of was conditionally squashing $_ to "". So i went several times through perlfunc, but found nothing useful. I completely forgot to go through perlop, where I might have found that $_ x= :-( 2) It would be so much nicer if I could use $_ instead of $a because it would allow to start to be three chars shorter: $_=aeiouy;s!!//c&y/!g the closest I got with that idea is: $_=aeiouy;s!!--&~y-!g;print eval"grep\$|$_\n--c,<>" (hard newline), which is again 50 chars. And while writing this text, i in fact found: $_=aeiouy;s!!\n\$|--&y-!g;print eval"grep$_--c,<>" and -n ($a=aeiouy)=~s!!\n\$|--&y-!g;eval"$a--c"&&print (hard newline) which are 49 (damnit).
