Home > other >  How can I remove all vowels in a sentence except for the vowels as the first letters in perl? [close
How can I remove all vowels in a sentence except for the vowels as the first letters in perl? [close

Time:09-22

What is wrong specifically with this code? How can I correct it?

$x = "without any vowels after the first letter\n";
foreach $i in @x[1..] {
    if $i "AEIOUaeiou";
    $x =~ tr/A E I O U a e i o u//d;
}
print "$x\n";

I tried [1..] to exclude the first letter. If it does not work, how else can I remove the first letter?

CodePudding user response:

First, most of that is not Perl, or any programming language for that matter. I'd suggest to work through a Perl tutorial of your choice first, before trying to get solutions for specific problems. However, here's an answer since the problem itself is of enough interest in general.

Next, in Perl you can't directly index into a string, so you can't skip the first character(s) like that.

But you can separate that first character in the string and process the rest (removing vowels), of course. One way with regex

use warnings;
use strict;
use feature 'say';

my $str = shift // 'out with ALL vowels after first';

$str =~ s/.\K(.*)/ $1 =~ tr{[aeiouAEIOU]}{}dr /e;

say $str;    #-->  ot wth LL vwls ftr frst

This cheats a little by using the /e modifier, which makes it so that the replacement side is evaluated as code, and running an independent transliteration (tr) there.

Then we need the /r modifier in that embedded tr/regex, to return the new string instead of changing the old one in place -- what wouldn't be possible anyway as one can't change $1.

One can also use a regex insteda of tr, less efficient but with its many conveniences

$str =~ s/.\K(.*)/ $1 =~ s{[aeiou]}{}igr /e;

Now we can use far more sophisticated tools in that regex than in tr; in this case it's only the i flag, for case-insensitive.

If it were more than the one first character change . to .{N}.


Regex is not compulsory, of course. A more elementary take: split the string into its first character and the rest, then use tr on the rest

# ...warnings, strict, feature 'say'...
    
my $str = shift // q(out with ALL vowels after first);

my ($result, $rest) = split //, $str, 2;  # first char, rest of string

$result .= $rest =~ tr/aeiouAEIOU//dr;  # prune $rest of vowels, append to $result

say $result;

Then put this in a little mini subroutine. To change the original string in place, instead of getting a new ($result) string, use it ($str) everywhere instead of $result.

I am not sure about how it compares efficiency wise but it may well fare well.

Out of curiosity, here it is in a single statement

$str = join '', map { length > 1 ? tr/aeiouAEIOU//dr : $_ }  split //, $str, 2;

This specifically uses the fact that only the first (one) character need be skipped; that is easily made dynamical, as long as the criterion does involve the length of substrings.

More importantly, this assumes that the rest of the string is longer than 1 character. To drop that assumption change the criterion

use feature 'state';

$str = join '', map { 
    state $chr_cnt = 0; 
      $chr_cnt > 1 ? tr/aeiouAEIOU//dr : $_ 
} 
split //, $str, 2;

This also relies on leaving aside just one character.

A more generic solution, which uses the property of substr to be possible to write to

substr($str, 1) =~ tr/aeiouAEIOU//d;

Here it's much cleaner and simpler to relax the limitation to the first character: just change that 1 in order to skip more characters (or, really, make it into a variable). But the tricky part here is that normally builtins can't be written to like that.

CodePudding user response:

The algorithm for solution of the problem is in your question

  • add letter to a string if it isn't vowel
  • add letter to the string if it is first vowel in the input string
use strict;
use warnings;

my $x = "without any vowels after the first letter\n";
my($o,$count) = ('',0);

print 'IN:  ' . $x;

for ( split('',$x) ) {
    $o .= $_ unless $count != 0 and /[aeiou]/i;
    $count   if /[aeiou]/i;
}

print 'OUT: ' . $o;

Output

IN:  without any vowels after the first letter
OUT: witht ny vwls ftr th frst lttr

Addendum: OP's clarification of the problem

  • look at each word in the sentence
  • if a word starts from vowel then delete all vowels but first one
  • if a word starts from none vowel then delete all vowels
use strict;
use warnings;
use feature 'say';

my $x = 'I like apples more than oranges';
my @o;

say 'IN:  ' . $x;

for ( split(' ', $x) ) {
    if ( /^[aeiou]/i ) {
        s/.\K(.*)/$1 =~ tr|aeiouAEIOU||dr/e;
    } else {
        tr|aeiouAEIOU||d;
    }
    @o = (@o,$_);
}

say 'OUT: ' . join(' ', @o);

Output

IN:  I like apples more than oranges
OUT: I lk appls mr thn orngs

Or in perlish style

use strict;
use warnings;
use feature 'say';

my $x = "I like apples more than oranges";

say 'IN:  ' . $x;
say 'OUT: ' . join(' ', map { s/.\K(.*)/$1 =~ tr|aeiouAEIOU||dr/e && $_ } split('[ ] ', $x));

Output

IN:  I like apples more than oranges
OUT: I lk appls mr thn orngs
  •  Tags:  
  • perl
  • Related