Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Re: Perl program to look into the phone directory and in case of a match, print the name along with the number

by Marshall (Canon)
on Feb 12, 2017 at 19:21 UTC ( [id://1181858]=note: print w/replies, xml ) Need Help??


in reply to Perl program to look into the phone directory and in case of a match, print the name along with the number

Your code appears to do one heck of a lot of unnecessary work! My brain started hurting trying to figure it out. Rather than making suggestions to your code, it was easier for me to just re-code it in the hopes that this will be instructive to you.

I guess it is nice that the number of phone book entries appears first. However, I consider it "noise" and throw it away. There is no need to use it and this would not be known in any "real" phone book application.

A valid line consists of a name, some space and optionally a phone number.

If we have the optional phone number, then this line is a new phone book entry. If it is just a name, then just look up the number in the phone_num hash and print the result. There is no need to "keep track of the sections".

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %phone_num; while (my $line =<DATA>) { my $name; my $phone; # this throws away malformed lines, like "4 (represents..." next unless ($name,$phone) = $line =~/^\s*([a-zA-Z]+)\s+(\d+)?/; if (defined $phone) # A new phone book entry { $phone_num{$name} = $phone; } else # Just a name. Look up its phone number { if ($phone_num{$name}) { print "$name $phone_num{$name}\n"; } else { print "Not found\n"; } } } print "\nJust to show what phone_num hash is:\n"; print Dumper \%phone_num; =Program Output: Not found john 334455 Not found Just to show what phone_num hash is: $VAR1 = { 'harry' => '112233', 'tom' => '332211', 'ryan' => '445566', 'john' => '334455' }; =cut __DATA__ 4 (represents no. of entries in phone directory) tom 332211 harry 112233 ryan 445566 john 334455 jack john jay 4576654 this is also junk line (just a number)
Now of course in a "real" application, this would be more complicated because for example you would have to allow for there to be more than one "john" each with a different phone number.

I hope this gets your thinking moving in more of a "Perl direction". Have Fun.

As a comment, I encourage you to space the code out, using appropriate comments and sometimes just a blank line to separate "thought units". White space consumes no MIPs! But that and comments can be some of the most important "code" that you write!

  • Comment on Re: Perl program to look into the phone directory and in case of a match, print the name along with the number
  • Download Code

Replies are listed 'Best First'.
Re^2: Perl program to look into the phone directory and in case of a match, print the name along with the number
by dk27 (Novice) on Feb 14, 2017 at 09:56 UTC

    Thanks Marshall for the valuable input. Your code is crisp and easy to understand. I appreciate the tips provided by you, Corion and Hauke Although I am not able to figure out why it doesnt display the result for the last name from STDIN. In the example shown, the result for last element "john" is missing.

    Input: 4 tom 332211 harry 112233 ryan 445566 john 334455 jay harry ryan kelly john Output: Not found harry=112233 ryan=445566 Not found Code: use strict; use warnings; use Data::Dumper; my %phone_num; while (my $line = <STDIN>) { my $name; my $phone; next unless ($name, $phone) = $line =~/^\s*([a-zA-Z]+)\s+(\d+)*/; if (defined $phone) # A new phone book entry { $phone_num{$name} = $phone; } else # Just a name. { if ($phone_num{$name}) { print "$name=$phone_num{$name}\n"; } else { print "Not found\n"; } } }

    The reason why I used a for loop inside a for loop was to get the correct value index to properly map name value pairs. Once I spliced the array N, I only have the names which needs to be checked for phonebook entry inside @N. Then I checked @N against the keys of the hash inside @data. Now the index i for @N need not be same for @data, that's why used another for loop to match the correct key-value pairs once the if condition i.e. grep holds good. This kind of approach makes it a lot more cumbersome. Your algo and approach seems to be way better and fast. Thanks!

      Hi dk27,

      In the example shown, the result for last element "john" is missing.

      Marshall seems to be correct in that the problem occurs when the file does not end on a newline character, however the final line should still be read even without the newline - confirm by printing $line before the next statement. The problem appears (at least to me) to be your regular expression instead. Note that \s+ requires there to be whitespace after the name, even for phone book queries, which have a name only. In most lines of input, that whitespace is the newline at the end of the line, since you're not chomping the lines, but the last line is missing the newline so it does not match the regular expression.

      Try this regular expression instead, it works for me: /^\s*([a-zA-Z]+)(?:\s+(\d+))?/

      Hope this helps,
      -- Hauke D

        Hauke D, you are completely right!

        My regex required at least one "space" character after the "name". A line ending ("\n" in Perl lingo) counts as a "space" character although in Windows "\n" might actually be a couple of characters.

        This regex also appears to work.

        next unless ($name,$phone) = $line =~/^\s*([a-zA-Z]+)\s*(\d+)?/;
        Thanks haukex!

        Hi Hauke,
        The regex you suggested works for me and qualifies for all other test cases.
        Correct me if I am wrong but when you used the operator "?:", this is used to form a non-capturing group so that \s+ is explicitly not captured while checking for regex and this makes it a match to the last test case and matches that for execution right?
        Thanks!

      I was able to re-create your symptom on my Win XP platform if the last line "john" does not have a line ending. In other words if the file EOF (End Of File) occurs right after "john" instead of there being a normal "end of line" sequence of characters before the EOF.

      my $line =<STDIN> is a line oriented I/O method. It returns a line when it sees the EOL (End Of Line) character sequence. If EOF happens before EOL, then it (added to post: should return the line as it does with C readline) However, in my case this returns "false" so this last "not quite complete line" is not processed.

      At the moment, I do not know how to get this unusual "john" last line processed. I am sure that there is a way, but right now I don't know it. Of course in your texteditor, end the john line with "enter" and all will be fine.

      Update: The symptoms that I am seeing above are on an ancient Win XP laptop. The result completely surprised me! It would be helpful if the OP can replicate my results.

      Update2: Hauke D got it right at Re^3: Perl program to look into the phone directory and in case of a match, print the name along with the number

        > If EOF happens before EOL, then it returns "false" so this last "not quite complete line" is not processed.

        Such a behaviour would be strange, inconsistent, and against the documentation, see readline:

        > each call reads and returns the next line until end-of-file is reached, whereupon the subsequent call returns undef .

        I'd report it as a bug.

        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1181858]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-04-19 13:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found