Reverse Vowels
Monday, February 12, 2024
Our task today is to “reverse vowels of a string”. This sounds like (and probably is) a coding interview question as well as a LeetCode problem, a Codewars kata, and the second task in the Perl Weekly Challenge #254.
If you don’t want spoilers, maybe stop reading here!
We are going to use Factor to solve this problem as well as a variant that is a bit more challenging.
Let’s Reverse The Vowels
One of the benefits of the monorepo approach that we have taken to building the extensive Factor standard library is developing higher-level words that solve specific kind of tasks.
One of those is arg-where – currently in the miscellaneous sequences.extras vocabulary – which we can use to find all the indices in a string that contain a vowel?:
IN: scratchpad "hello" [ vowel? ] arg-where .
V{ 1 4 }
We’ll want to group the beginning and ending indices, ignoring the middle index if the number of indices is odd since it would not change:
: split-indices ( indices -- head tail )
dup length 2/ [ head-slice ] [ tail-slice* ] 2bi ;
We can then build a word to reverse specified indices:
: reverse-indices ( str indices -- str )
split-indices <reversed> [ pick exchange ] 2each ;
And then use it to reverse the vowels:
: reverse-vowels ( str -- str )
dup >lower [ vowel? ] arg-where reverse-indices ;
And see how it works:
IN: scratchpad "factor" reverse-vowels .
"foctar"
IN: scratchpad "concatenative" reverse-vowels .
"cencitanetavo"
Pretty cool!
Let’s Reverse The Vowels, Maintain The Case
A somewhat more challenging task is to reverse the vowels, and to swap their letter case.
Let’s start by building a word to swap the case of two letters:
: swap-case ( a b -- a' b' )
2dup [ letter? ] bi@ 2array {
{ { t f } [ [ ch>upper ] [ ch>lower ] bi* ] }
{ { f t } [ [ ch>lower ] [ ch>upper ] bi* ] }
[ drop ]
} case ;
And then another word to exchange two indices, but also swap their case:
: exchange-case ( i j seq -- )
[ '[ _ nth ] bi@ swap-case ]
[ '[ _ set-nth ] bi@ ] 3bi ; inline
A word to reverse the indices, but also swap their case:
: reverse-indices-case ( str indices -- str )
split-indices <reversed> [ pick exchange-case ] 2each ;
And, finally, a word to reverse the vowels, but also swap their case:
: reverse-vowels-case ( str -- str )
dup >lower [ vowel? ] arg-where reverse-indices-case ;
And then see how it works:
IN: scratchpad "FActor" reverse-vowels-case .
"FOctar"
A pretty fun problem!