Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active June 7, 2022 17:49
Show Gist options
  • Save ericnormand/66a3ddaafc7c61dcb0a857aa88239142 to your computer and use it in GitHub Desktop.
Save ericnormand/66a3ddaafc7c61dcb0a857aa88239142 to your computer and use it in GitHub Desktop.
470 Eric Normand Newsletter

Reverse words

Write a function that takes a string containing words (one or more sentences) and returns a string containing the words in reverse order.

Examples

(reverse-words "my name is Eric.") ;;=> "Eric. is my name"
(reverse-words "hello") ;;=> "hello"
(reverse-words "I love you") ;;=> "you love I"

Note: Words are characters separated by whitespace.

Thanks to this site for the problem idea, where it is rated Very Hard in Java. The problem has been modified.

Please submit your solutions as comments on this gist.

To subscribe: https://ericnormand.me/newsletter

@jonasseglare
Copy link

(defn reverse-words [s]
  (->> s
       (partition-by #(Character/isWhitespace %))
       reverse
       (transduce cat str)))

@miner
Copy link

miner commented May 29, 2022

(require '[clojure.string :as str])

(defn reverse-words [s]
  (str/join " " (rseq (str/split s #"\s+"))))

@miner
Copy link

miner commented May 29, 2022

Correction on examples: (reverse-words "my name is Eric.") ;;=> "Eric. is name my"

@sexptherapy
Copy link

Minor stylistic variant of @miner's answer just for fun, as my original answer was just the same (only difference being the usage of reverse instead of rseq).

  • Name the intermediary step of splitting the string into words
  • Use POSIX character class for whitespace regexp
  • Pass the space character itself instead of a string containing a space to str/join

I like that the core of the function reads just the same as the function name (reverse words). I find it also interesting to consider whether long-form representations like \space or #"\p{Space}+" (that aren't idiomatic in Clojure) instead of " " or #"\s+" can aid in legibility or not. Of course here it is but a tiny little function where it doesn't really matter much.

(require '[clojure.string :as str])

(defn reverse-words [s]
  (let [words (str/split s #"\p{Space}+")]
    (str/join \space (reverse words))))

@PEZ
Copy link

PEZ commented May 30, 2022

  (defn reverse-words [s]
    (->> (clojure.string/split s #"\s+")
         reverse
         (clojure.string/join " ")))

I guess it gets interesting if reversing words is the only thing that should happen.

@miner
Copy link

miner commented May 30, 2022

(rseq rev) returns, in constant time, a seq of the items in rev (which can be a vector or sorted-map), in reverse order. reverse is generally slower (proportional to the length of the collection). For small collections, it doesn't matter. However, it's a good idea to use rseq when you can.

@harwood19
Copy link

harwood19 commented May 30, 2022

I'm new to Eric and relatively new to clojure, this being my first exercise all around as a recent subscriber. Also new to github. But hey, took my best shot. Figured my submission was lazy at best but wanted to ante up. Good to know 'reverse' is less efficient, though like normalizing a database maybe sometimes designing with redundancy / inefficiency ends up being a net-positive for readability / accessibility given the power available these days? Still a slippery slope, no doubt. End of a day - better and more simplified, elegant code seems the best way to sleep at night. Fun lil community. Thanks for having me! -Matthew Leach, VT

@choffee
Copy link

choffee commented May 31, 2022

Here's what I got. Look similar to the others. (Noted about rseq being faster, thanks @miner.)

(defn reverse-words [words]
    (str/join " " (reverse (str/split words #" "))))

Also, as above my split assumes only single space and not tab. It does keep the spacing in the return string but not sure if that's a feature or a bug :)

@KingCode
Copy link

KingCode commented Jun 2, 2022

Here is a little variant which preserves white space between tokens.
EDIT @jonasseglare, I like your solution which also preserves spacing, but has a simpler approach (no corner cases, unlike mine).

Also thanks to @miner for the rseq comment, I started using it. From the documentation, it looks like the trade-off b/w reverse/rseq is convenience vs performance, as reverse seems to allow any seqable including regular vanilla maps, and rseq requires a sorted structure (actually a reversible?, but most/all sorted? implementations also implement clojure.lang.Reversible). Fun stuff!

(require '[clojure.string :refer [split]])

(defn reverse-words [txt]
  (let [rspaces (-> txt (split #"[^\s]+") rest reverse (conj "")) 
        rwords (->> (split txt #"\s+") rseq)]
    (->> rwords (interleave rspaces) (apply str))))


(reverse-words "my name is Eric.") ;;=> "Eric. is name my"
(reverse-words "hello") ;;=> "hello"
(reverse-words "I love you") ;;=> "you love I"
(reverse-words "I,  love you");;=>"you love  I,"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment