It's not that I think perl will be significantly (or at all) nicer, but I think the readability difference is often overstated and I'd like to test that thought.
def Factorial(x):
output = 1
for i in xrange(x):
output *= (i + 1)
return output
def Factorial2(x):
return reduce(operator.mul, xrange(1, x + 1), 1)
I provided two versions, both with constant memory, to show how it'd look with explicit iteration and without it.With this, I get:
>>> Factorial(200)
788657867364790503552363213932185062295135977687173263294742533244359449963403342920304284011984623904177212138919638830257642790242637105061926624952829931113462857270763317237396988943922445621451664240254033291864131227428294853277524242407573903240321257405579568660226031904170324062351700858796178922222789623703897374720000000000000000000000000000000000000000000000000L
Exponentiation: def Power(a, b):
if not b:
return 1
if b % 2:
return Power(a, b - 1) * a
x = Power(a, b/2)
return x * x
Add a constant: lambda x: x + 7
Or, if you want to name it globally: def TweakValue(x):
return x + 7
How do they look in Perl?First one (not 100% sure it's constant memory, I think perl optimises the for to avoid instantiating the (1..$n) list:
#!/usr/bin/perl
use Modern::Perl;
use bigint;
say fact(5000);
sub fact {
my ($n) = @_;
my $output = 1;
for my $i (1..$n) {
$output *= $i;
}
return $output;
}
(Took the value up to 5000 to get something which ran long enough to get a measurement, on my laptop it runs (including startup time) in ~1.2s. (Can we compare runtime too? I know we're discussing readability, but I'm interested).The reduce version (again, I'm unsure of const mem req). Comes in at ~1.4s:
#!/usr/bin/perl
use Modern::Perl;
use List::Util qw(reduce);
use bigint;
say fact(5000);
sub fact {
my ($n) = @_;
return reduce(sub { $a * $b; }, 1, (1..$n));
}
The add a constant (true lambda, closing over lexicals in scope, etc etc) is 'sub': sub { my ($x) = @_; $x + 7; }
(doesn't have the python lambda limitations). (Be aware that perl GC is refcounted though, so ref cycles are possible).Edit: I think the languages are comparable in terms of features and also readability. People may dislike leading sigils and that's fair, but I dislike python's "no need to declare your vars, just hope you don't typo an assignment" approach to lexicals.
For example:
return reduce(sub { $a * $b; }, 1, (1..$n));
would be: reduce { $a * $b } 1..$n;
This my $output = 1;
for my $i (1..$n) {
$output *= $i;
}
would be: $output *= $_ for (1..$n)
So, I have a question for you(assuming you write perl for a living). Is this your preferred style, or you do this to appease readability police which demands programming languages be readable by non programmers? Because really, what kind of programmer programs in perl and is averse to $_? $output *= $_ for (1..$n)
and you're right to call me on it. If I am writing a multi-line for loop, I do like to name the loop var though.I think this is an interesting point (in a discussion about readability), do non-perl coders think the one-liner (with 'for' suffix) is more or less readable?
(I also prefer explicit return for maintenance reasons, except possibly in one-line subs)
It does since 5.005 - so for a good long time now :-)
The reduce version (again, I'm unsure of const mem req). Comes in at ~1.4s:
This would allocate the array I'm afraid. You'd could use something liek List::Gen's reduce() to get around that.
def function(x, y, z):
versus
sub function { my ($x, $y, $x) = @_; }
The overhead is only really the 'my = @_;' chars, but it is annoying. On the other hand, it does allow you to do things like partially unpack args and pass the rest as a bundle to a super class.
def method(self, *args):
a, b = args[:2]
super(self).method(*args[:2])
Perl's lack of using the definition for the variables doesn't necessarily make it 'more powerful.'And with @_, you're free to use positional arguments, named arguments, variable arity, etc. without penalty. Some languages only give you some of that.
use Method::Signatures::Simple;
http://search.cpan.org/~rhesa/Method-Signatures-Simple-1.02/...But you might instead want to look at this (or similar with Moose support) to get simple type declarations:
http://search.cpan.org/~barefoot/Method-Signatures-20120523/...
use bignum;
use v5.10;
=cut
def Factorial(x):
output = 1
for i in xrange(x):
output *= (i + 1)
return output
=cut
# Direct translation
sub factorial {
my $x = shift;
my $output = 1;
for my $i (1 .. $x) {
$output *= $i;
}
return $output;
}
say factorial(200);
=cut
def Factorial2(x):
return reduce(operator.mul, xrange(1, x + 1), 1)
=cut
use List::Util "reduce";
sub factorial2 {
my $x = shift;
reduce { $a * $b } (1 .. $x);
}
say factorial(200);
=cut
def Power(a, b):
if not b:
return 1
if b % 2:
return Power(a, b - 1) * a
x = Power(a, b/2)
return x * x
=cut
sub power {
my $a = shift;
my $b = shift // return 1;
if ($b % 2) {
return power($a, $b - 1) * $a
}
my $x = power($a, $b/2);
return $x * $x;
}
# lambda x: x + 7
my $plus7 = sub { $_[0] + 7 };
# Or:
my $plus7 = sub { my $x = shift; $x + 7 };
say $plus7->(14);
=cut
def TweakValue(x):
return x + 7
=cut
sub plus7 { $_[0] + 7 }
say plus7(14); sub factorial($n) {
my $output = 1;
for 1..$n -> $i {
$output *= $i;
}
$output;
}
or sub factorial2($n) {
[*] 1..$n;
}
Pretty anonymous function to add a constant: -> $x { $x + 7 }
Really short version: * + 7
Named: sub plus7($x) {
$x + 7;
}Umm, did you mean:
def factorial(x):
output = 1
for i in xrange(2, x):
output *= i
return output
Here are the perl versions. Such trivial examples are going to look the same in languages which share the same paradigms. use List::Util qw(reduce);
# This is how I would write it.
sub fact1 {
my $num = shift;
my $output = 1;
$output *= $_ for (2 .. $num);
return $output;
}
# Most people don't prefer inline loops and $_ variable, though
# I don't see why not.
sub fact2 {
my $num = shift;
my $output = 1;
for my $i (2 .. $num) {
$output *= $i;
}
return $output;
}
sub fact3 {
my $num = shift;
return reduce { $a * $b } 2..$num
}Yes. That makes my Python example incorrect(should be xrange(2, num + 1))
> 1..^$x in Perl 6, don't recall if that's the Perl 5 syntax or not.
Perl 5 doesn have 1..^$x. I have always used 1..($n - 1) where I needed it - don't know if there is a better alternative.
Or just xrange(num) and then, as in my original code, output *= num + 1. :-)
HN ate your asterisk(it uses asterisk to mark italics), or something wrong with my chrome app for HN. In your post, I saw:
def Factorial(x): output = 1 for i in xrange(x): output = (i + 1) return outputEnglish does look simpler than French, too. On the other hand, once you get beyond the accents, you know how to pronounce French, whereas that's not the case with English (cf. "ghoti"). Personally, I never was that bothered by e.g. the type characters, and quite often they were quite helpful about the context expected/required. But then again, I'm German, so maybe growing up with a somewhat ridiculous grammar and Funky Capitalization helped (there's a Perl module that allows you to write code in Latin, and here the cases and other grammatical structures replace the funky characters).
Perl certainly has its weaknesses. Some operations should probably be bound to types/objects instead of C-like functions, references are often difficult to untangle and the default object system ain't that grand (on the other hand: Moose). But I've yet to find a language that doesn't have similar weaknesses. Due to the somewhat funky syntax, they're just quite obvious. What does that dollar sign right there indicate? On what default variable is this code operating? Schwartzian what? But I would call that "traceability" issues. And this certainly also happens with macros, meta-programming, complicated object hierarchies, generators, decorators, DoI containers, etc.
It probably does matter for casual programmers, i.e. people who don't work 20+ hours/week in Perl. Sysadmins come to mind. But if your involvement with the language is beyond that, I think "readability" is quite often a matter of taste, not more. Which, of course, doesn't totally disqualify this argument. It's just more a matter of rap vs. punk, not junk food vs. rucola salad.
Although I'm not sure whether describing Perl as the Vietnamese of programming languages would be a good selling point, so I'll stick with French ;)
And the number 3 is different from your fingers being number now than they were before.
Not sure about 'familiar' though.
Perl is my set of power tools that sit in my basement for weeks until I get them out and fix/make/do something. And what the OP said is correct. I can install new versions and run 10 year old code without issues.
These days all the cool languages have funny syntax (hell, Ruby lifted most of perl's weirdness directly), sometimes (as with coffeescript) it's touted as a feature. So go back and look at perl with modern eyes. I think you'll find most of the conventional wisdom about its readability was more about the conventions than the wisdom.
Readability and reasonable shoot-self-in-foot protection are not optional. See the article from earlier today about not catering to power users. Same concept. You can't really design a language for the top 5% of programmers and expect it to gain any sort of acceptance. (See: Haskell, Ocaml)
Perl is a power tool. The reason it can be unreadable is because of it's terse syntax, wherein resides its power. If you value readability above power then you choose Java, and I would agree that for a large enterprise app developed by an army of mediocre developers then Java is surely the better choice. However to judge the absolute merits of a language like Perl on reputed readability, without taking into consideration what its terseness allows, and how it works for large scale development given a sane coding standard is just burying your head in the sand.
You will be shocked how few tools are there for the other kind of data.
Basically - it's a balance - don't be too dumb, but don't be too clever either. I'd submit Python as being solidly in the middle ground here.
Your average PHP web dev is not the cutoff for the 95th percentile. But yes, I can imagine them trying to understand monads. I have also observed them succeeding at understanding monads. They aren't hard to learn, just something you need to learn. Being able to learn simple things doesn't make you the top 5%.