Peter
Peter Campbell Smith

Triumvirate and Treamviruti?

Weekly challenge 254 — 29 January 2024

Week 254 - 29 Jan 2024

Task 2

Task — Reverse vowels

You are given a string, $s. Write a script to reverse all the vowels (a, e, i, o, u) in the given string.

Examples


Example 1
Input: $s = "Raku"
Output: "Ruka"

Example 2
Input: $s = "Perl"
Output: "Perl"

Example 3
Input: $s = "Julia"
Output: "Jaliu"

Example 4
Input: $s = "Uiua"
Output: "Auiu"

Analysis

If there is a one-liner for this then I'm afraid it has eluded me.

My solution starts by converting the input string into an array of individual letters.

I then set two pointers to the start and end of the array and move each inward until they are both pointing at a vowel, at which point I switch the two vowels.

I repeat that until the pointers meet, and then convert the array back into a string.

It would be possible to do this without converting the string to an array by using the fact that substr() can be an lvalue, so for example you could use substr($s, $p1, 1) = substr($s, $p2, 1). You could also skip the switching of each pair of vowels if they were in fact the same vowel (eg in 'noon') but the savings for either of these will be only few microseconds, if anything.

Try it 

Try running the script with any input:



example: antidisestablishmentarianism

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2024-01-29
use utf8;     # Week 254 task 2 - Reverse vowels
use strict;   # Peter Campbell Smith
use warnings; 
binmode STDOUT, ':utf8';

reverse_vowels('triumvirate');
reverse_vowels('bcdf');
reverse_vowels('bcdfa');
reverse_vowels('abcdf');
reverse_vowels('aeiou');
reverse_vowels('aeiiou');
reverse_vowels('supercalifragilisticexpialidocious');

sub reverse_vowels {
    
    my ($s, $p1, $p2, $x, @letters);
    
    # initialise
    $s = lc(shift); 
    @letters = split('', $s);
    $p1 = 0;
    $p2 = scalar(@letters) - 1;
    
    # loop while left pointer left of right pointer
    LOOP: while ($p1 < $p2) {
        
        # if left pointer has found a vowel
        if ($letters[$p1] =~ m|[aeiou]|) {
            
            # wind back right pointer until it meets a vowel or left pointer
            while ($p1 < $p2) {
                
                # right pointer has found a vowel, so switch left and right
                if ($letters[$p2] =~ m|[aeiou]|) {
                    $x = $letters[$p2];
                    $letters[$p2 --] = $letters[$p1];
                    $letters[$p1 ++] = $x;
                    next LOOP;
                
                # not a vowel, move right pointer to the left
                } else {
                    $p2 --;
                }
            }
            
        # not a vowel, so move left pointer to the right    
        } else {
            $p1 ++;
        }
    }   
    say qq[\nInput:  \$s = '$s'\nOutput:      '] . join('', @letters), qq['];   
}

Output


Input:  $s = 'triumvirate'
Output:      'treamviruti'

Input:  $s = 'bcdf'
Output:      'bcdf'

Input:  $s = 'bcdfa'
Output:      'bcdfa'

Input:  $s = 'abcdf'
Output:      'abcdf'

Input:  $s = 'aeiou'
Output:      'uoiea'

Input:  $s = 'aeiiou'
Output:      'uoiiea'

Input:  $s = 'supercalifragilisticexpialidocious'
Output:      'suporcilofrigalistecixpiiladicaeus'