sub f

I haven't really experimented with Perl's subroutines yet, so I figured I would write a simple factorial function to get a feel for them. Perl is strange in that you don't define the arguments of your functions; they're simply passed to the special array @_. So here was my first attempt:

sub factorial {
    $x = 1;
    for (1..@_[0]) {
        $x *= $_;
    }
    return $x;
}

Clean and easy to understand. But Perl isn't about nice, orderly code, it's about obfuscation and brevity, dammit! Good code should look like a code. Here's the result when I tried to shorten this code as much as possible:

sub f{$x=1;for(1..pop){$x*=$_}$x}

Instead of using @_[0], I used pop, which operates on @_ by default. You'll also note that return is missing; subroutines will automatically return the last statement evaluated, so we simply evaluate $x.

This reduction process is a hobby called golfing. Perl is a great language for golfing, as you might infer from its motto ("There's more than one way to do it"), and the results are often quite obfuscated, which makes them fun to pick apart.

We're at 33 characters now, but I think we can go shorter. I tried rewriting the program in recursion form to see if I could make it more compact:

sub f {
    if (@_[0] > 2) {
        return @_[0] * f(@_[0] - 1);
    else {
        return 1;
}

Which can be reduced to:

sub f{@_[0]?@_[0]*f(@_[0]-1):1}

Much better. If you aren't familiar with conditionals (?:), you can probably make a stab at how they work without resorting to google. Here, the condition is simply a number; in Perl, there are no booleans, only zero and non-zero. So the second statement is only evaluated when @_[0] == 0. (Unfortunately, we can't use pop here -- it would be called more than once.)

31 characters now. Can we go shorter? Sure. Who needs loops anyway?

sub f{eval join'*',1..pop}

Here we use join? to create a string, then evaluate it.
Of course, a significant reduction in size will often come at the expense of reduced functionality; that last program won't return anything if you ask for the factorial of something less than 1, and the recursion program loops infinitely if you pass in a negative number (because they're still non-zero). But that doesn't really matter. What matters is that we wrote a factorial function in 26 characters. For reference, here's the shortest factorial program in C++ that I could find (40 characters):

int f(int x){return(x<=1)?1:(x*f(x-1));}

and Java (54 characters):

public static int f(int x){return((x<=1)?1:x*f(x-1));}  

Amateurs.

Leave a Reply

Theme: Esquire by Matthew Buchanan.