Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Final Print Statement in Foreach Loop Prints Twice

by ScarletRoxanne (Initiate)
on Nov 11, 2018 at 08:04 UTC ( [id://1225564]=perlquestion: print w/replies, xml ) Need Help??

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

I'm trying to write a very simple script that takes two words from STDIN and outputs TRUE if they're anagrams and FALSE if not. My main issue is that if the two words aren't anagrams (this is the final "else" statement in the script), the output looks like: Sorry, that's not an anagram pair Sorry, that's not an anagram pair

where I just want:

Sorry, that's not an anagram pair

Other, more minor issues for the especially generous:

1. I know what the FALSE values are for Perl, but I can't get the script to print FALSE by, for example, setting a variable to '' or 0, etc. or saying return ''. Ideally, I wouldn't have to put "print TRUE/FALSE" in the script at all.

2. I put in the last elsif statement in the script to see if it would affect the printing twice problem. It didn't, and now I'm curious why my m// expression doesn't work. It's supposed to find pairs that are identical except that one has more whitespace than the other.

Here's the script! I'm sorry it's so long - again, the problem is at the very end with the final "else" statement. Many thanks!!!

#To Run: Type start.pl on the command line. #The script should prompt you to enter a word or phrase. #Once you've done that, it'll prompt you for another one. #Then you will be told if the two terms are anagrams or not. #!/usr/bin/perl -w use strict; #I have to use this to make STDIN work. IDK why. $|=1; #variables my $aWord; my $bWord; my $word; my $sortWord; my $sortWords; my @words; my %anaHash; print "\nReady to play the anagram game? Excellent.\n\nType your first + word or phrase, then hit Enter.\n\n"; $aWord = <STDIN>; chomp $aWord; print "\n\nThanks! Now type your second word or phrase and hit Enter.\ +n\n"; $bWord = <STDIN>; chomp $bWord; #This foreach loop performs the following tasks: #1. Pushes the two words from STDIN into an array (unsure if this is r +eally necessary) #2. lowercases everything and removes all characters except for letter +s & spaces #3. splits both words into characters, sorts them alphabetically, then + joins the sorted letters into a single "word" #4.pushes the array into a hash @words = ($bWord, $aWord); foreach $word (@words) { $word =~ tr/A-Z/a-z/; $word =~ s/[^a-z ]//ig; $sortWord = join '', sort(split(//, $word)); push @{$anaHash{$sortWord}}, $word; } #This foreach loop tries to determine if the word pairs are anagrams o +r not. foreach $sortWords (values %anaHash) { #"if you see the same word twice AND the input was two identical w +ords:" if (1 < @$sortWords && @$sortWords[0] eq @$sortWords[1]) { print "\n\nFALSE: Your phrases are identical!\n\n"; } #"if you see the same word twice AND the input was two different w +ords (i.e. a real anagram):" elsif (1 < @$sortWords && @$sortWords[0] ne @$sortWords[1]) { print "\n\nTRUE: @$sortWords[0] and @$sortWords[1] are anagrams!\n +\n"; } #this is a failed attempt to identify pairs that are identical exc +ept one has extra spaces. Right now, this fails and falls into the "e +lse" category below. elsif (@$sortWords[0] =~ m/ +@$sortWords[-1]/ || @$sortWords[-1] =~ m/ +@$sortWords[0]/) { print "\n\FALSE: @$sortWords[0] and @$sortWords[-1] are NOT anagra +ms. Spaces are characters, too!\n\n"; } #This is supposed to identify anything that's not an acronym. But +the output prints twice! It's maddening!!!! else { print "Sorry, that's not an anagram pair\n"; } }

restored content by Discipulus

Replies are listed 'Best First'.
Re: Final Print Statement in Foreach Loop Prints Twice
by haukex (Archbishop) on Nov 11, 2018 at 08:44 UTC

    Hello ScarletRoxanne, welcome to PerlMonks and Perl!

    See #4 on the Basic debugging checklist: print your data structures. For example, if I add use Data::Dump; dd \%anaHash; just after the first loop, and enter Hello and World, the output I get is:

    { dlorw => ["world"], ehllo => ["hello"] }

    Whereas if I enter restful and fluster, I get:

    { eflrstu => ["fluster", "restful"] }

    Now if you look at your second loop, you're iterating over values %anaHash, so I hope it becomes clear why the loop is executing twice?

    I'm not exactly sure about what your requirements are, but I'm not sure why you need the second loop? Checking that the phrases are identical could be done with eq on $aWord and $bWord, and whether or not the words are anagrams could be checked by looking at the number of keys in the hash, no?

    Some more general tips:

    • Instead of #!/usr/bin/perl -w, do use warnings; (What's wrong with -w and $^W Update: Link fixed, thanks Laurent_R!).
    • $|=1; I have to use this to make STDIN work. IDK why. - I'm not sure either, $| doesn't have to do with STDIN, I removed it and it worked fine on my end.
    • It's better if you don't declare all your variables at the top of the script, because then they're not much better than global variables. Instead, declare them at the smallest scope they are needed, such as foreach my $sortWords (...) or my $sortWord = join '', ...
    • $word =~ tr/A-Z/a-z/; - you might want to look at the lc function.
    • @words = ($bWord, $aWord); foreach $word (@words) can be written as foreach $word ($bWord, $aWord).
    • $aWord = <STDIN>; chomp $aWord; is fine, but is more commonly written as chomp( my $aWord = <STDIN> );.
    I can't get the script to print FALSE by, for example, setting a variable to '' or 0, etc. or saying return ''. Ideally, I wouldn't have to put "print TRUE/FALSE" in the script at all.

    I'm not sure what you mean by this - Perl doesn't have any "TRUE" and "FALSE" constants like other languages, instead there are simply a couple of values that Perl interprets as being "false" in a "boolean context" (for example, in an if (...) condition) - see Truth and Falsehood. These values don't get converted internally into anything else. If you want Perl to print the strings TRUE and FALSE, you'll need to tell it to do that, such as in print $value?"TRUE":"FALSE".

    I'm curious why my m// expression doesn't work.

    If I enter hello and hell o, then the regular expressions will be / +hello/ and / +hell o/. Because both of them require spaces before the h, which neither of the input strings have, neither regex will match. I suspect you may have wanted to match on the keys instead, but even then, I think you need to rethink the overall approach, because there would still be further modifications needed to make it work with regexes. (Hint: I would've made a copy of the strings and removed all the spaces.) Another regex tip: If you don't anchor your regular expressions with ^ and $, then remember that the regular expression will match anywhere in the string (e.g. "foobar"=~/a/ is true).

    Update 2019-08-17: Updated the link to "Truth and Falsehood".

Putting back the content of the OP: Final Print Statement in Foreach Loop Prints Twice
by Laurent_R (Canon) on Nov 11, 2018 at 12:50 UTC
    The following is the original post by ScarletRoxanne who, for some reason, decided to remove it. I'm quoting it here so that the answers provided by various monks make some sense.

    Discipulus used the following text to restore the OP (thanks Laurent_R) and added a readmore

Shorter script
by Laurent_R (Canon) on Nov 11, 2018 at 12:38 UTC
    Here's the script! I'm sorry it's so long
    Indeed, 72 lines for finding out if words are anagrams is a bit long. But it's not very difficult to make it significantly shorter:
    #!/usr/bin/perl use strict; use warnings; use feature 'say'; say "\nReady to play the anagram game? Excellent.\n\nType your first w +ord or phrase, then hit Enter.\n\n"; chomp (my $aWord = <STDIN>); say "\n\nThanks! Now type your second word or phrase and hit Enter.\n\ +n"; chomp (my $bWord = <STDIN>); say "You typed the same word or phrase twice" and exit if $aWord eq $b +Word; say is_anagram($aWord, $bWord) ? "True" : "False"; sub is_anagram { s/^ +| +$//g for @_; # remove leading or trailing spaces return 0 if length $_[0] != length $_[1]; # words are not anagrams + if not the same length my @words = map {join '', sort split //, lc} @_; return $words[0] eq $words[1]; }
    Update: it appears that you deleted your node while I was posting this answer. Why? The answers provided to you don't make sense anymore.
Re: Final Print Statement in Foreach Loop Prints Twice
by rnewsham (Curate) on Nov 11, 2018 at 08:34 UTC

    If the two words are not anagrams then %anaHash will have two keys as the $sortWord will be different for the two words entered. Which means you go around your for each twice for none anagrams and only once if they are anagrams.

    Depending on your future plans to expand on this script. You may be able to do away with the loop and replace it with an if which identifies the $sortWord is the same else print not an anagram. If you have a reason for the loop I would suggest moving the that else statement outside the loop and make it an if conditional on a $anagramFound flag populated by the earlier checks.

Re: Final Print Statement in Foreach Loop Prints Twice
by jdporter (Paladin) on Nov 22, 2018 at 19:27 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (4)
As of 2024-03-29 08:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found