简体   繁体   中英

Using Perl hash table to delete vowels in string, but output is always empty

I'm trying to write a short script in Perl to go through a an array of strings provided by the user, check in a hash table to see if there are vowels in the strings, then return the strings minus the vowels. I know this would be easier to accomplish using regex, but the parameters for the problem state that a hash table, exists() , and split() must be used. This is the script I have so far:

my @vowels = qw(a e i o u A E I O U);
my %vowel;
foreach $v (@vowels) {
   $vowel{$v} = undef;
}
foreach $word (@ARGV) {
   my @letter_array = split(undef,$word);
}
foreach $letter (@letter_array) {
   print($letter) if !exists($vowel{$letter})
}
print "\n"

Input: hello

Expected output: hll

Actual output: nothing

There are no error messages, so I know it's not a syntax error. Any ideas what I'm messing up? I'm much more comfortable with Python and this is one of my first attempts at Perl.

You should use strict not to mess visibility of your variables. If you require perl version 5.12 or higher it would be used automatically.

So your list @letter_array exists only in foreach my $word (@ARGV) loop. That's why it's empty in the end.

If you want to fix that you'll get the following code:

#!/usr/bin/env perl

use strict;
use warnings;

my @vowels = qw( a e i o u y A E I O U Y );
my %vowel;

foreach my $v (@vowels) {
   $vowel{$v} = undef;
}

my @letter_array;
foreach my $word (@ARGV) {
    @letter_array = split //, $word;
}

foreach my $letter (@letter_array) {
    print($letter) if !exists($vowel{$letter})
}

print "\n"

But this code is still not practical.

  1. If you would get more that 1 word in the input, you'll show only the last one, because the @ letter_array overwrites each time.
  2. You can use map to get the hash of vowels much easier without using extra variables.
  3. You can use less loops if you would handle each word right after reading it.
  4. You can also use unless if you want to check if not to make it prettier and more perl-style.
  5. Don't use split on undef. Better use split //, $word
  6. You can use for instead of foreach because it's the same but shorter:)

So you can get an optimised solution.

#!/usr/bin/env perl

use 5.012;
use warnings;


my %vowels = map { $_ => undef } qw( a e i o u y A E I O U Y );

for my $word (@ARGV) {
    my @letters = split //, $word;
    for my $letter (@letters) {
        print $letter unless exists $vowels{$letter};
    }
    print ' ';
}
print "\n"

Result:

$ perl delete_vowels.pl hello world
hll wrld

An alternative and more compact method of achieving the same thing is to use the substitute operator, " s " with a regular expression that matches the vowels.

Here is an example

use strict;
use warnings;

for my $word (@ARGV)
{
    print $word =~ s/[aeiou]//gri;
}

or more succinctly like this

use strict;
use warnings;

for (@ARGV)
{
    print s/[aeiou]//gri;
}

Key points to note

  1. the regular expression uses the Character Class [aeiou] to match a single lower-case vowel.
  2. the substitute operator has been given three options
    • the i option to force a case insensitive match. This means the Character Class [aeiou] will match both uppercase and lower-case vowels.
    • the g option to make the substitute match all instances of the regular expression -- in this instance it will match against all the vowels in the string.
    • the r option (which is a newish addition to Perl) to get the substitute operator to return the substituted string.

running that gives this

$ perl try.pl hello world
hllwrld

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM