Insert string every nth element in a list of strings - clojure

I'm new to Clojure.
I'm developing a tic tac toe game
I'm trying to make a function that "formats" the board, which is a map with the number of the position as key and the keywords :x :o and :e for values (:e stands for empty).
I want to insert a newline character every 3 in the list of the name of the keywords.
For example "x" "x" "x" "e" "e" "e" "e" "e" "e" should be converted to "x" "x" "x" "\n" "e" "e" "e" "\n" "e" "e" "e" then I would concatenate those strings so I can print it.
(defn- newline-every
[n list]
(if (empty? list)
[]
(let [[fst snd] (split-at n list)]
(concat
(conj fst "\n")
(newline-every n snd)))))

It's Clojure so there are surely many ways to do this in one line. Here's one attempt:
(flatten (interpose "\n" (partition n list))))
As user amalloy commented, there's never an excuse to use flatten, so here's a better way:
(apply concat (interpose ["\n"] (partition n list))))
Which gives, starting from the sequence of strings (which all contain one character) you gave:
... > (newline-every 3 ["x" "x" "x" "e" "e" "e" "e" "e" "e"])
("x" "x" "x" "\n" "e" "e" "e" "\n" "e" "e" "e")
You can then transform that into a string:
... > (apply str (newline-every 3 ["x" "x" "x" "e" "e" "e" "e" "e" "e"]))
"xxx\neee\neee"

Related

Explanation for combination of cycle drop and take in clojure

I am trying to understand the implementation of rotating a sequence to which the answer i find in git hub is below
(fn [n coll]
(take (count coll) (drop (mod n (count coll)) (cycle coll))))
Could you please explain what is exacty happening here
(take 6 (drop 1 (cycle ["a" "b" "c"])))
("b" "c" "a" "b" "c" "a")
How is this being produced
From the documentation of cycle:
Returns a lazy (infinite!) sequence of repetitions of the items in coll.
So in your example:
(cycle ["a" "b" "c"])
;; => ["a" "b" "c" "a" "b" "c" "a" "b" "c" "a" "b" "c" ...]
(toward infinity and beyond)
To cut down an infinite sequence, you have to use take which takes the first n element of a sequence. So:
(take 6 (cycle ["a" "b" "c"]))
;; => ["a" "b" "c" "a" "b" "c"]
In your example, just before calling take, you use drop which left out the first n element of a sequence. So:
(drop 1 (cycle ["a" "b" "c"]))
;; => ["b" "c" "a" "b" "c" "a" "b" "c" "a" "b" "c" ...]
(take 6 (drop 1 (cycle ["a" "b" "c"])))
;; => ["b" "c" "a" "b" "c" "a"]
You can learn more about lazy sequences from this chapter of "Clojure from the Brave and True".

Clojure: Manipulating certain items in list

I'm a Clojure newbie and having trouble with its immutable state. I'm trying to write a function that takes a list with the frames of a bowling game. For example, a list would look like ["X" "12" "2/" "X" "45" "X" "13" "33" "X" "81"]. I want the function to output a list that takes care of the frames that just have integers, and adds the numbers together. So, if the list above was inputted, the following list would be outputted: ["X" "3" "2/" "X" "9" "X" "4" "6" "X" "9"]. This was my attempt, but immutable state in Clojure is making it hard for me to understand how to go about this:
(defn eval-frames
[flist]
(loop [frames '()]
(if (not (= flist '()))
frames
(eval-normal (rest flist)))
(if (not (or (spare? flist) (strike? flist) (= flist ())))
(conj frames (+ (get (first flist) 0) ((get (first flist) 1))))
(conj frames (first flist)))
)
)
This just ends up outputting the first frame in the list, instead of the entire list. Any suggestions would be extremely appreciated!
There's a fair amount wrong with the code you posted:
The whole (if (not (= flist '())) part does nothing since you never use the results it gives back. You're thinking too imperative here. conj returns the "modified" list, it doesn't alter the original!
You never recur in the loop, so the loop only ever runs once.
I'd just use map for this. It iterates over a list, and "transforms" each element based on a function. I highly recommend getting as used to map and reduce as you can, because you'll be using them constantly.
My plan was this:
If all the characters in the frame are digits, sum the frame, else, leave the frame alone.
To sum the frame, I'm using parseLong to turn each character into a number, then(apply + to sum the parsed numbers, and then str to turn it back into a String.
(map
(fn [frame]
; Almost reads like English!
(if (every? #(Character/isDigit %) frame)
(->> frame ; Take the frame...
(map #(Long/parseLong (str %))) ; parse each character in the frame...
(apply +) ; then sum the parsed numbers...
(str)) ; and turn them back into a string.
frame)) ; Else, do nothing and leave the frame alone
["X" "12" "2/" "X" "45" "X" "13" "33" "X" "81"])
=> ("X" "3" "2/" "X" "9" "X" "4" "6" "X" "9")
This would have been simplified a bit if you had stored the scores as numbers originally instead of converting them to Strings. That would prevent the need for Long/parseLong, and the final call to str to turn each summed frame back into a String.
(map #(if (every? (fn [c] (<= (int \0) (int c) (int \9))) %)
(str (apply + (map read-string (map str %))))
%)
["X" "12" "2/" "X" "45" "X" "13" "33" "X" "81"])
or without the read-string:
(map #(if (every? (fn [c] (<= (int \0) (int c) (int \9))) %)
(str (apply + (map (fn [c] (- (int c) (int \0))) %)))
%)
["X" "12" "2/" "X" "45" "X" "13" "33" "X" "81"])
=> ("X" "3" "2/" "X" "9" "X" "4" "6" "X" "9")
(defn eval-frames [frames]
(map #(if (re-find #"\d\d" %)
(reduce + (map (fn [i](Integer. (str i))) (seq %))) %) frames))
So evaluating with a given frame would give :
(eval-frames ["X" "12" "2/" "X" "45" "X" "13" "33" "X" "81"])
=> ("X" "3" "2/" "X" "9" "X" "4" "6" "X" "9")

Roman numerals kata in clojure

I'm learning clojure and I wrote this code to resolve the roman numerals kata:
(def romans (sorted-map-by >
1000 "M"
500 "D"
400 "CD"
100 "C"
90 "XC"
50 "L"
40 "XL"
10 "X"
9 "IX"
5 "V"
4 "IV"
1 "I"))
(defn roman-digit [arabic]
(first (filter (fn [[key value]]
(>= arabic key)) romans)))
(defn arabic-to-roman [arabic]
(def roman (roman-digit arabic))
(if (> arabic 0)
(apply str (val roman) (arabic-to-roman (- arabic (key roman))))
""))
I want to know how could I get this code more efficient/idiomatic/clean. I'm sure I can to learn a lot of new stuff.
Thanks.
Here's my stab at it.
(defn roman-digit [arabic]
(first
(filter #(>= arabic (first %))
[[1000 "M" ]
[500 "D" ]
[400 "CD"]
[100 "C" ]
[90 "XC"]
[50 "L" ]
[40 "XL"]
[10 "X" ]
[9 "IX"]
[5 "V" ]
[4 "IV"]
[1 "I" ]])))
(defn arabic-to-roman [arabic]
(when (> arabic 0)
(let [[arabic-diff roman] (roman-digit arabic)]
(apply str roman (arabic-to-roman (- arabic arabic-diff))))))
What's going on here?
When you have an ordered closed set of values that you eventually use as sequence anyway, using a vector of pairs in the right order needs much less ceremony.
Never do def inside a condition like this. Think of def as declaration and definition of a global variable (symbol). For local scopes (bindings) use let.
Prefer when over if especially if there is only one branch. Note that both str and apply do the right thing for nil (which is returned at the bottom of the recursion when arabic is 0).

Zip two lists in clojure into list of concatenated strings

Instead of zip-mapping two lists to get:
(zipmap ["a","b","c"] ["c","d","e"]) = {"c" "e", "b" "d", "a" "c"}
I want to concatenate the first element of the first list with the first element of the second list and so on to get:
("ce","bd","ac")
or in the reverse order.
You can do that with map. map can take multiple collections, it takes the next element from each collection and passes them into the function passed as the first argument (stopping when one of the collections runs out). So you can pass in a function that takes n arguments, and n collections.
The expression
(map str ["a" "b" "c"] ["c" "d" "e"])
will call str first with "a" and "c", then with "b" and "d", then with "c" and "e". The result will be
("ac" "bd" "ce")
Since str can takes a variable number of arguments it can be used with any number of collections. Passing in four collections, like
(map str ["a" "b" "c"] ["d" "e" "f"] ["g" "h" "i"] ["j" "k" "l"])
will evaluate to
("adgj" "behk" "cfil")

Simple comparator does not sort as (I) expected

I expected this code snippet to produce the original vector, but sorted in a case-insensitive way. Instead I get the original vector untouched. Why doesn't my comparator work?
user=> (ns user (require [clojure.contrib.string :as str]))
nil
user=> (sort
(comparator #(compare (str/upper-case %1) (str/upper-case %2)))
["B" "a" "c" "F" "r" "E"])
("B" "a" "c" "F" "r" "E")
comparator returns a java.util.Comparator when given a predicate (a function which returns true or false). You don't need it if you're using compare explicitly. So just:
(sort #(compare (str/upper-case %1) (str/upper-case %2))
["B" "a" "c" "F" "r" "E"])
;=> ("a" "B" "c" "E" "F" "r")
Alternatively, use sort-by:
(sort-by str/upper-case ["B" "a" "c" "F" "r" "E"])
;=> ("a" "B" "c" "E" "F" "r")
compare is not a predicate, it's a comparator.