Java/Perl

Ovid on 2008-05-09T09:11:58

For a party game, needed to repeatedly pick random letters from the (ASCII) alphabet. I had my laptop, so I just did it. A friend, a Java programmer, was amazed. My favorite part was when he said "you're going to type out all 26 letters, aren't you?" Um, no I'm not.

perl -le "print ['a' .. 'z']->[rand(26)] while "

For those not quite getting it, here's the shortest I could think of in Java (without getting to the point of obfuscation and with the caveat that they're functionally equivalent, though internally they handle things in a moderately different way):

import java.io.*;

class Pick {
    public static void main(String[] args) {
        String letters    = "abcdefghijklmnopqrstuvwxyz";
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        while (true) {
            try {
                br.readLine();
            }
            catch (IOException e) {
                System.out.println("Caught exception trying to readline: " + e);
                System.exit(1);
            }
            int rand = (int)(Math.random() * 26);
            System.out.println(letters.charAt(rand));
        }
    }
}

Of course, then you have to compile it and run it as separate steps.

Can anyone post any common code snippets which express themselves more naturally in Java than Perl? (Libraries which can be readily duplicated in Perl don't count)


also a favourite

pfig on 2008-05-09T10:47:06

i usually write that as

perl -le 'print chr(65+rand(26))'

your version is prettier, though :)

(sorry, i'm not a java person)

Re:also a favourite

Ovid on 2008-05-09T11:24:33

Damn. I thought my version is crystal clear, but yours is just perfect and obvious -- if you know ASCII.

And doing that in Java:

import java.io.*;

class Pick {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            try {
                br.readLine();
            }
            catch (IOException e) {
                System.out.println("Caught exception trying to readline: " + e);
                System.exit(1);
            }
            System.out.println((char)( 65 + (int)(Math.random() * 26)));
        }
    }
}

Re:also a favourite

rjbs on 2008-05-09T12:07:42

I Don't Speak Java, But:

If this is for a party game, isn't the exception handling gratuitous? Whether it fails with a stack track or a nice error message, it's screwed up. Can't you just omit that?

Why do you need the reader at all? Is there some requirement that you must read from stdin if you want to send to stdout?

Re:also a favourite

Ovid on 2008-05-09T12:29:48

The reason for the reader is that the game were were playing required we think of words for categories but each word had to start with a particular letter. The reader was there to ensure we could just hit "Enter" and get the next letter. So if we just rerun java Pick every time, we can get it to this:

class Pick {
    public static void main(String[] args) {
        System.out.println((char)( 65 + (int)(Math.random() * 26)));
    }
}

This doesn't replicate the exact functionality of the Perl, though, which is really what the post was about. If I want the 'hit enter for a new letter' functionality, the minimum I can write seems to be this:

import java.io.*;

class Pick {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            try {
                br.readLine();
            }
            catch (Exception e) {}
            System.out.println((char)( 65 + (int)(Math.random() * 26)));
        }
    }
}

I've stubbed out the exception (a notorious but common practice) because that's a checked exception and failure to catch it is a compile time error. Checked exceptions are so common that you wind up with fun boilerplate like this:

public ValidationConsumer (EventConsumer next) {
    super (next);
    setContentHandler (this);
    setDTDHandler (this);
    try { setProperty (DECL_HANDLER, this); }
    catch (Exception e) { /* "can't happen" */ }
    try { setProperty (LEXICAL_HANDLER, this); }
    catch (Exception e) { /* "can't happen" */ }
}

Re:also a favourite

jdavidb on 2008-05-09T13:26:55

As Ovid points out, you do have to do something about the exception because it is a checked exception. But my preferred choice in this case would be to declare that main() throws the exceptions, thus letting the calling environment handle it, because, as you say, if it's screwed up, it's screwed up, no matter how it chooses to express the failure.

Ovid, did you know you could throw exceptions from main()?

How much have we shortened your Java code, now? :)

Re:also a favourite

rjbs on 2008-05-09T15:12:00

I had never heard of checked exceptions. I can see their value, but I think I'd find the more irritating than useful unless I could disable them for one-offs.

Thanks!

Re:also a favourite

Ovid on 2008-05-09T15:24:15

Checked exceptions are evil. They sound good, until you try them. Bruce Eckel, the author of Thinking in Java, has a great essay about the problems with checked exceptions.

Re:also a favourite

rlpeacock on 2008-05-09T17:40:11

Checked exceptions are extremely valuable when designing or using APIs. Most of the pain comes from misuse. Used properly, you only have to deal with them when you really should write error handling anyway. But I didn't create this account to enter that endless debate. I just wanted to point out that its in the nature of the difference strengths of Java and Perl that you can't post a piece of code where Java is more natural. Its only when dealing with large bodies of code that Java's strength is apparent.

Re:also a favourite

chromatic on 2008-05-09T17:57:25

Its only when dealing with large bodies of code that Java's strength is apparent.

Funny; I consider Java's encouragement to produce large bodies of code a disadvantage.

Re:also a favourite

jdavidb on 2008-05-09T18:19:53

Okay, I got a half-million lines of Java here, judging by just a second ago when I did a find . -name '*.java' | xargs wc -l. When do the advantages start becoming apparent?

(Okay, to be fair, I can see some advantages. But if I were starting this from scratch, yes, it would be Perl!)

Re:also a favourite

jdavidb on 2008-05-09T13:23:43

That can come across as slightly more documented if you say:

perl -le 'print chr(ord('A')+rand(26))'

Then you don't have the potentially mystifying 65. (I fess up; I always have to look up the value of 'A' on an ASCII chart whenever I want to do anything like this.)

Some languages will basically treat 'A' as an int and allow you to drop the ord(). Not Perl. Might work for Java, though.

Re:also a favourite

Aristotle on 2008-05-09T23:41:10

Some languages will basically treat ‘A’ as an int and allow you to drop the ord(). Not Perl.

perl -le'$a="A";$a++for 1..rand 26;print$a'

even easier

slanning on 2008-05-09T11:31:53

Note: for some reason, I've never been the life of the party....

You're generating random letters: what does that mean? Say someone asks me for a random letter. I say 'e'. They ask again for a random letter, I say 'e' again. They ask again, I say 'e'. Then I start saying 'e' even before they ask. In an annoying whiny voice.

And I repeat this for the duration of our natural lives.

What makes this sequence of perhaps ONE MILLION 'e's less random than a sequence of letters generated by a computer program?

Re:even easier

Aristotle on 2008-05-09T23:47:51

The probability that such a sequence is the result of a truly random process is infinitely much smaller than the probability of winning the lottery and being hit by lightning on the same day. This is known as a statistical impossibility: it could happen, but the odds are stacked so high against it that it never does.

Re:even easier

slanning on 2008-05-15T15:07:28

I agree with you, naturally.

But I thought this might be amusing in this context: photoshop of Dilbert comic. (not that I'm making fun of Debian or anything :)

Using the shell

jdavidb on 2008-05-09T13:19:19

It could be shortened if you're allowed to pipe it to head or a pager. Then you could dispense with the input part and just put it in an infinite loop. I'm not sure if using the shell and other commands means you've gone out of the realm of "accomplishing this in Java" or not, but it is more or less functionally equivalent.

Golf

Juerd on 2008-05-09T17:28:35

Usually I end up writing golfish code like this:

perl -ple'$_=(a..z)[rand 26]'

Politics!

pudge on 2008-05-15T19:15:48

We recently had our county legislative district caucuses for the Republican Party. The party rules say, in some cases, in the event of a tie, to decide the winner by lot. We had a 7-way tie (with each potential delegate assigned a number), for 6 positions (and since this was for alternate delegates, we needed them in a specific order).

So I whipped out my laptop and wrote something along the lines of:

perl -le '@a=qw(6 23 57 72 75 78 80); print $a[rand @a]'
Got a winner, then removed that winner from @a and kept going until we had the 6 positions filled. Sure, I could have made it a little smarter to remove the winner for me and repeat, but that would have taken a bit more time, and everyone was waiting. :-) Though now that I think about it, I could have used:

perl -le '@a=qw(6 23 57 72 75 78 80); for (1..6) { print splice, @a, int(rand @a), 1 }'

Re:Politics!

Aristotle on 2008-05-15T21:52:13

That has a subtle bias: picking 1 out of 6 after picking 1 out of 7 does not have the same probability distribution as picking 2 out of 7. The stochastically unbiased way would be thus:

perl -MList::Util=shuffle -le'print +(shuffle 6 23 57 72 75 78 80)[0..5]'

Re:Politics!

Aristotle on 2008-05-15T21:53:25

Err, make that print for (...).

Re:Politics!

pudge on 2008-05-15T22:24:43

Also, qw(...). :-)

Re:Politics!

pudge on 2008-05-15T22:24:21

That has a subtle bias: picking 1 out of 6 after picking 1 out of 7 does not have the same probability distribution as picking 2 out of 7.

True, but it doesn't need to. The traditional way to do these things, which is to draw straws, eliminating one person at a time, has the same bias. Indeed, I am not so sure that your way is better: we literally do have a 7-way tie for one place, and then after eliminating the 7th place, we have a 6-way tie for the next place.

The rules give us no guidance either way, but I think my method is closer to the spirit of the thing (which is why it is how it is). I doubt that anyone would complain with either method.

Re:Politics!

jdavidb on 2008-05-16T15:52:29

I like you're method better simply because the algorithm is more readily open for inspection. Not that you can't check out the contents of the module in the other method, but with yours what happened is up front.

Re:Politics!

Aristotle on 2008-05-16T16:36:57

If you really wanted to be sure the algorithm is correct, in either case, you’d have to inspect how rand works…

Writing a Fisher-Yates shuffle (which is what’s in List::Util) by hand is easy: you step through the list, swapping each element with any random following element or itself (this bit is crucial to avoid bias). It was just quicker to use the one in List::Util, and much less prone to errors in the haste of dashing off a one-liner.