I find it easier to read languages which use brackets for breaking up blocks of code. For example C/C++, C#, D, and Perl.
The language which is in common use today, that I really find difficult to understand, is shell scripting. Plus the keyword you use to end a construct isn't always the mirror of the beginning keyword. I don't think anyone would use it if it wasn't already so widely used.
READABLE:
Perl
if( $anything == 1 ){
# some code
}elsif( $anything == 2 ){
# some code
}else{
# some more code
}
UNREADABLE:
Shell
if [ $a == z* ] # File globbing and word splitting take place.
then
if [[ $a == z* ]] # True if $a starts with an "z" (regex pattern matching).
then
echo do-something # But only if both conditions are valid.
fi
fi
I don't know about you, but I keep wanting to end the if block like this:
if [ $Jack && $Beanstalk ]
then
echo plant beanstalk
fee fi fo fum
Further
Those people who automatically say Perl is a terrible language, generally assume that it is unreadable because they take one look at the Regex sub-language, and assume there is no possible way to rewrite it in a cleaner way. Which is completely wrong. The first thing you must learn is of the /x modifier, which allows whitespace embedded in the Regex to be used to separate out different parts of the Regex.
So we can turn this:
/^([+-]?)(\d+\.\d+|\d+\.|\.\d+|\d+)([eE][+-]?\d+)?$/;
into this:
/
^
([+-]?) # first, match an optional sign
( # then match integers or f.p. mantissas:
\d+\.\d+ # mantissa of the form a.b
|\d+\. # mantissa of the form a.
|\.\d+ # mantissa of the form .b
|\d+ # integer of the form a
)
([eE][+-]?\d+)? # finally, optionally match an exponent
$
/x;
Which I might add (as far as I know) is not available in any other general purpose Regular Expression librarys, accessible within other languages. So in other words if you are using another language you are stuck with the first form, or you have to write significantly more code to achieve the same ends, which will be error prone. See here for more information.
Actually Perl can be very powerful, imagine running arbitrary code, from within a Regex. Don't worry you're not allowed to use interpolation with this feature, by default. Also this feature, last I heard is experimental, although I would bet it will stay in the language in some form or another. I know for a fact, it is a feature of Perl 6.
$x =~ /(?{print "Hi Mom!";})/; # matches,
# prints 'Hi Mom!'
Yet another useful feature:
Named back-references
( copied directly from perldoc perlretut )
Perl 5.10 also introduced named capture buffers and named backreferences. To attach a name to a capturing group, you write either (?<name>...)
or (?'name'...)
. The backreference may then be written as \g{name}
. It is permissible to attach the same name to more than one group, but then only the leftmost one of the eponymous set can be referenced. Outside of the pattern a named capture buffer is accessible through the %+
hash.
Assuming that we have to match calendar dates which may be given in one of the three formats yyyy-mm-dd
, mm/dd/yyyy
or dd.mm.yyy
y, we can write three suitable patterns where we use 'day'
, 'month'
and 'year'
respectively as the names of the buffers capturing the pertaining components of a date. The matching operation combines the three patterns as alternatives:
$fmt1 = '(?<year>\d\d\d\d)-(?<mon>\d\d)-(?<day>\d\d)';
$fmt2 = '(?<mon>\d\d)/(?<day>\d\d)/(?<year>\d\d\d\d)';
$fmt3 = '(?<day>\d\d)\.(?<mon>\d\d)\.(?<year>\d\d\d\d)';
for my $d qw( 2006-10-21 15.01.2007 10/31/2005 ){
if ( $d =~ m{$fmt1|$fmt2|$fmt3} ){
print "day=$+{day} month=$+{mon} year=$+{year}\n";
}
}
prints:
day=21 month=10 year=2006
day=15 month=01 year=2007
day=31 month=10 year=2005
If any of the alternatives matches, the hash %+
is bound to contain the three key-value pairs.
If we take the number matching Regex from above and convert it to a Perl 6 Grammar, it becomes even easier to read.
Perl 6
grammar Date {
rule year{ \d{4} }
rule day{ \d{1,2} }
rule month{ \d{1,2} }
rule ymd{ <year>-<month>-<day> }
rule mdy{ <month>/<day>/<year> }
rule dmy{ <day>.<month>.<year> }
rule match{
<(ymd)>
|<(mdy)>
|<(dmy)>
}
}
Note: I probably have some mistakes in this last bit of code, due to the Perl 6 design changing, and because I also haven't written a lot of Perl 6 code yet. It is also a bit of a contrived example
You would also need to change the for loop also.
for [qw( 2006-10-21 15.01.2007 10/31/2005 )] -> $d {
if ( $d =~ Date.match ){
print "day=$<day> month=$<mon> year=$<year>\n";
}
}
prints:
day=21 month=10 year=2006
day=15 month=01 year=2007
day=31 month=10 year=2005