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.
Nice little drill. Here is my take on it using Julia 1.1.1: https://pastebin.com/WsrMBkKD. Cheers!
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{$_->[0]}sort{$a->[1]<=>$b->[1]}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{$_->[0]} ## Pull out the letters sort{$a->[1]<=>$b->[1]} ## 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!Just tweaked it – that was a bit too long…
> echo Programming Praxis is fun! | perl -pe '$_=join"",map{/[a-z]/i?$_^uc|join"",map{$_->[0]}sort{$a->[1]<=>$b->[1]}map{[$_,rand]}split//,uc:$_}split/([a-z]+)/i' Igoapmrrngm Xsriap si fnu!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:
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"; } }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.
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:
Here’s a Haskell version.
[…] the previous exercise, we wrote a program to scramble the letters in each word of a string, retaining capitalization and […]
[…] 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 […]