## Scrambled Words

### October 8, 2019

Given a string, scramble the letters of each word individually, where a word is a maximal sequence of letters, maintaining the original capitalization and punctuation. For instance, the string “Programming Praxis is fun!” might be scrambled as “Grprnimaogm Srxpia is unf!”

Your task is to write a program to scramble the letters of each word in a string. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

Pages: 1 2

### 10 Responses to “Scrambled Words”

1. Zack said

Nice little drill. Here is my take on it using Julia 1.1.1: https://pastebin.com/WsrMBkKD. Cheers!

2. James Smith said

Favourite language again – this time a bit golfish – so I apologies in advance

```> echo Programming Praxis is fun! | perl -pe '\$_=join"",map{m{[a-z]}i?\$_^uc|join"",map{\$_->}sort{\$a-><=>\$b->}map{[\$_,rand]}split//,uc:\$_}split/([^a-z]+)/i'
Igoapmrrngm Xsriap si fnu!
```

Expanding this out a bit – with comments…

```join "",
map{
m{[a-z]}i  ## First part (?) is for the words
? \$_ ^ uc |  ## Contains " " if the letter is lower case and NULL (chr 0)
## if upper case
## Note uc with no parameter actions on the variable \$_
## "OR"ing the string (with |) with the spaces and nulls
## makes the appropriate letters lower case!
join "",                ## Join back into a word
map{\$_->}            ## Pull out the letters
sort{\$a-><=>\$b->} ## Schwartzian transform to sort letters
map{[\$_,rand]}          ## Add a random index - which we will sort!
split//, uc             ## Split into individual characters
: \$_         ## Second part (:) if for the non-word parts of the sentance
}
split/([^a-z]+)/i ## Split into chunks containing words and gaps!
## as using perl -pe - runs on the rows of the
## file passed into the script!
Igoapmrrngm Xsriap si fnu!
```
3. James Smith said

Just tweaked it – that was a bit too long…

```> echo Programming Praxis is fun! | perl -pe '\$_=join"",map{/[a-z]/i?\$_^uc|join"",map{\$_->}sort{\$a-><=>\$b->}map{[\$_,rand]}split//,uc:\$_}split/([a-z]+)/i'
Igoapmrrngm Xsriap si fnu!
```
4. chaw said

Here is a solution in R7RS Scheme, emphasizing clarity over length or
efficiency. It is quite similar to @programmingpraxis’s solution, but
has no external dependencies (other than a couple of procedures from
the popular SRFIs 1 and 27).

``` (import (scheme base) (scheme write) (scheme char) (only (srfi 1) list-tabulate) (only (srfi 27) random-integer)) (define (vector-swap! v i j) (let ((tmp (vector-ref v i))) (vector-set! v i (vector-ref v j)) (vector-set! v j tmp))) ;; A random permutation of [0,n-1] (define (random-permutation n) (let ((v (make-vector n))) (do ((i (- n 1) (- i 1))) ((< i 0)) (vector-set! v i i)) (do ((i (- n 1) (- i 1))) ((< i 0) v) (vector-swap! v i (random-integer (+ i 1)))))) (define (scramble-chars/case-by-position lst) (let* ((vec (list->vector lst)) (n (vector-length vec)) (rvec (random-permutation n))) (list-tabulate n (lambda (i) ((if (char-upper-case? (vector-ref vec i)) char-upcase char-downcase) (vector-ref vec (vector-ref rvec i))))))) (define (scramble-words str) (let loop ((ichars (string->list str)) (ochars '()) (wchars '())) (if (null? ichars) (list->string (reverse (append (scramble-chars/case-by-position wchars) ochars))) (if (char-alphabetic? (car ichars)) (loop (cdr ichars) ochars (cons (car ichars) wchars)) (loop (cdr ichars) (cons (car ichars) (append (scramble-chars/case-by-position wchars) ochars)) '()))))) (display (scramble-words "Programming Praxis is fun!")) (newline) ```

Output:

``` Gomrganpmri Paxris si fnu! ```

5. matthew said

A straightforward C++ solution, using library functions for scanning strings and shuffling – I’d originally used a FSA, but using find_if etc. is quite neat:

```#include <algorithm>
#include <iostream>
#include <cctype>

int main() {
std::string t = "Programming Praxis is fun!";
for (int i = 0; i < 10; i++) {
auto s = t;
for (auto p = s.begin(), q = s.end(); ; p++) {
auto r = find_if(p, q, isalpha);
if (r == q) break;
p = find_if_not(r+1, q, isalpha);
std::random_shuffle(r, p);
if (p == q) break;
}
for (auto i = 0U; i < s.size(); i++) {
s[i] = (isupper(t[i]) ? toupper : tolower)(s[i]);
}
std::cout << s << "\n";
}
}
```
```Rgmipaormng Raxpis si nfu!
Ronagrmpimg Sxiarp si nuf!
Poianmrgrmg Ipaxrs si fun!
Imggnorpram Rsxaip is nfu!
Mgamgionrrp Xpasir is unf!
Immrnparggo Paxirs is fun!
Gimornmparg Xpasir is fun!
Pmriorggman Rpaxsi is ufn!
Prgigonamrm Rsxpai is fnu!
Rampngmogir Piaxrs is fnu!
```
6. matthew said

I probably should have included the string header there and I’m not sure why the compiler didn’t complain about missing the std:: for find_if and find_if_not.

7. Daniel said

Here’s a solution in Python.

```import random
import re

def scramble(s):
chars = list(s.lower())
for word in re.finditer(r'(\w)+', s):
i, j = word.span()
chars[i:j] = random.sample(chars[i:j], j-i)
chars = [a.upper() if b.isupper() else a for a, b in zip(chars, s)]
return ''.join(chars)

s = 'Programming Praxis is fun!'
print(scramble(s))
```

Output:

```Rgimpomagrn Xapris si nuf!
```
8. Globules said

```import Control.Monad ((>=>), forM_)
import Data.Char (isLetter, isUpper, toLower, toUpper)
import Data.List.Split (condense, split, whenElt)
import System.Environment (getArgs)
import System.Random.Shuffle (shuffleM)

-- Transform a string by scrambling the order of each sequence of letters, but
-- otherwise retain the overall order of characters.
scramble :: R.MonadRandom m => String -> m String
scramble xs = fmap (recap xs . concat) . mapM shufLets . spl \$ xs

-- Split a string into sequences of letters and non-letters.
spl :: String -> [String]
spl = split (condense \$ whenElt \$ not . isLetter)

-- Shuffle the string only if it begins with a letter.
shufLets :: R.MonadRandom m => String -> m String
shufLets xxs@(x:_) = if isLetter x then shuffleM xxs else pure xxs
shufLets xxs       = return xxs

-- Capitalize the the second argument based on the case of the first.
recap :: String -> String -> String
recap = zipWith step
where step x y = if isUpper x then toUpper y else toLower y

main :: IO ()
main = do
args <- getArgs
forM_ args \$ scramble >=> putStrLn
```
```\$ ./scramble "Programming Praxis, ç'est ben l'fun!"
Gigprammnro Arixps, ç'ets ebn l'ufn!
```
9. […] the previous exercise, we wrote a program to scramble the letters in each word of a string, retaining capitalization and […]

10. […] program to scramble the letters of each word in a string. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments […]