gortok has asked for the wisdom of the Perl Monks concerning the following question:

Having learned Perl syntax as a 2nd language, I still have problems making expressions and statements more concise. For example, in a recent (still developing) script for comparing Active Directory accounts to an employee list, I needed to make a function that would take an employee name (Smith, John), and convert it to the AD nomenclature (JSmith). This is the code I used, and I'm sure it's not as brief or concise as it could be. I was hoping for enlightenment, and how you managed to overcome this hurdle if Perl wasn't your [0]th language.
for ($i = 0; $i <= $#sortedNames; $i++) { @temp = split/,/, $sortedNames[$i][0]; $temp[1] = reverse $temp[1]; $x = chop($temp[1]); $toOutput = reverse $temp[0]; $toOutput .= $x; $output = reverse $toOutput; print NEWFILE "$output\n"; }
I would include the rest of the script, but it looks so bad that I wouldn't want to burn your eyes. Any comments are welcome. (Yes, I didn't use strict;. Yes, I should use strict;). This is only part of the script, I could include the rest, though I'd have to 'clean' it up, as it looks far worse than the above snippet.

Replies are listed 'Best First'.
Re: Perl 'grammar'
by ikegami (Patriarch) on Jan 08, 2008 at 22:58 UTC
    • You will rarely need C-style loops

      for ($i = 0; $i <= $#sortedNames; $i++) {

      If you need a counting loop, use the much more readable (and just as efficient)

      for my $i (0..$#sortedNames) {

      Or when iterating over a list or array, you can use

      for my $name (@sortedNames) {
    • Why use reverse to prepend a string?

      $toOutput = reverse $temp[0]; $toOutput .= $x; $output = reverse $toOutput;

      is the same as

      $output = $x . $temp[0];
      Thank you for your insight. I've been away from Perl for too long. Far too long.
Re: Perl 'grammar'
by toolic (Bishop) on Jan 08, 2008 at 21:02 UTC
    I would tend to use substr rather than reverse to abbreviate the first name in this case:
    #!/usr/bin/env perl use warnings; use strict; while (<DATA>) { my ($last, $first) = split /,/; $last =~ s/\s+//; $first =~ s/\s+//; print substr($first, 0, 1), $last, "\n"; } __DATA__ Smith, John Bar, Foo

    Prints out:

    JSmith FBar
      Why not let the split regex do some of the work of removing spaces?

      my ( $last, $first ) = split m{\s*,\s*};

      Cheers,

      JohnGG

Re: Perl 'grammar'
by gamache (Friar) on Jan 08, 2008 at 21:10 UTC
    Here is a Perlish way to convert a name into AD format:
    sub format_name_for_AD { my $empl_name = shift; # $empl_name has the form "Last, + First" my ($last, $first) = $empl_name=~/^([^,]+), (.+)/ or return undef; return uc(substr($first, 0, 1)) . ucfirst($last); } for ('Smith, John', 'Wall, Larry', 'Perlis, Alan', 'Ritchie, Dennis') +{ print format_name_for_AD($_), "\n"; }
    I'll leave what's going on as an exercise to the reader (hint: perlre), but as a short comment, I'll say that string matching in Perl shouldn't look like string matching in C. :)
      why is
      my $empl_name = shift; my ($last, $first) = $empl_name=~/^([^,]+), (.+)/ or return undef;
      preferred over
      my ($last, $first) = /(.*), (.*)/ or return undef;
      (or perhaps /(.*),\s*(.*)/ ?)
        Hell, why not sub format_name_for_AD {/(.+), (.)/ ? $2.$1 : undef}?
        The two are not the same. Consider what happens when the sub is called as format_name_for_AD($foo).
Re: Perl 'grammar'
by dragonchild (Archbishop) on Jan 08, 2008 at 22:16 UTC
    In C, you have to work with each item in itself. With Perl, you work with the whole thing at once. So, you have an idea of what you're starting and where you want to go.
    # Starting with Lastname, Firstname # Wanting to get to FLastname sub transform { my ($name) = @_; $name =~ s/^\s*(\S+),\s(\S)/${2}${1}/; return $name; }
    So, grab what you want from what you're given, transform it into what you want, then return it. It's often best to do this sort of thing in a function because it's easier to be destructive and smaller scopes give you greater flexibility. You don't have to watch out for anyone else's feelings. :-)

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      Not quite.. :-)
      That returns JSmithohn
      sub transform { my ($name) = @_; $name =~ s/^\s*(\S+),\s(\S)\S+$/${2}${1}/; # Added '\S+$' return $name; }
        Nice catch. A better version of the regex would be s/^\s*(\S+),\s+(\S).*$/${2}${1}/; - you don't want to assume too much about the item. That's why the ^\s*(\S+) instead of just ^(\S+).

        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Perl 'grammar'
by jdporter (Paladin) on Jan 09, 2008 at 02:44 UTC
    use strict; my @fullnames = ( "Smith, David", "Lamb, Mary", "Depp, Johnny", "Child, Julia", ); my %x = map { /(.*), (.)/ ? ( $_ => $2.$1 ) : () } @fullnames; print "$_ => $x{$_}\n" for keys %x;

    Oh, I'm sorry — you said if Perl wasn't my [0]th language.
    ;-)

    A word spoken in Mind will reach its own level, in the objective world, by its own weight
Re: Perl 'grammar'
by memnoch (Scribe) on Jan 08, 2008 at 21:11 UTC
    The following assumes that the input names are formatted correctly. (If not you might want to look at the lc and ucfirst functions.)
    use strict; my @fullnames = ("Smith, David", "Lamb, Mary", "Depp, Johnny", "Child, + Julia"); foreach my $fullname (@fullnames) { my ($last, $first) = map {s/^\s*//; $_} split /,/, $fullname; $first =~ s/^(\w).*/$1/; my $id = $first . $last; print "\$id = $id\n"; }
    memnoch

A reply falls below the community's threshold of quality. You may see it by logging in.