Context
I want to generate all characters that can be generated by:
opening note pad
pressing a single key on the keyboard
holding shift + pressing a single key on the keyboard
What I currently have:
(concat (range (int \a) (int \z))
(range (int \A) (int \Z))
(range (int \0) (int \9)))
then manually appending more characters like ~!##$%^&*()_+{}|:"<>?,./;'[]\
Question
Is there a more elegant way of doing this?
Edits
Yes, I'm referring to US Qwerty keyboard.
If you look at a US ASCII chart, it seems that all the characters you want are within (range 33 127). So the simplest way to get a sequence of all those characters is to convert that range to characters.
(map char (range 33 127))
But if you are trying to validate that a string contains only those characters, have a function like:
(defn valid-char? [c]
(let [i (int c)]
(and (> i 32) (< i 127))))
Then you can use it with every? to validate a string:
user=> (every? valid-char? "hello world")
true
user=> (every? valid-char? "héllo world")
false
Using the following map form will generate the characters you want.
(map #(str (char %)) (range 32 127))
Related
I'm working through the 4clojure problems. I came across this answer for #120, which I would have totally never thought of on my own:
(fn sum-square [coll]
(let [digits (fn [n] (map #(- (int %) 48) (str n)))
square #(* % %)
sum-digits (fn [n] (reduce + (map square (digits n))))]
(count (filter #(< % (sum-digits %)) coll))))
The part I'm really trying to understand is how the digits part of it works.
(fn [n] (map #(- (int %) 48) (str n))
I'm really confused about how
(map #(- (int %) 48) "10")
returns
(1 0)
Can you please explain how this works? I'm confused about why n has to be turned into a string, and why it's then turned back into an integer, and why it has 48 subtracted. I'm sure there must be some really neat trick I'm missing.
Thanks!
"10" in the context of map can be treated as a seq of chars(in this case \1 and \0)
then int converts say \1 to ascii 49, and \0 to ascii 48
then - 48 converts 49 to 1 and 48 to 0
(defn shuffle-letters
[word]
(let [letters (clojure.string/split word #"")
shuffled-letters (shuffle letters)]
(clojure.string/join "" shuffled-letters)))
But if you put in "test" you can get "test" back sometimes.
How to modify the code to be sure that output will never be equal to input.
I feel embarrassing, I can solve it easily in Python, but Clojure is so different to me...
Thank you.
P.S. I thing we can close the topic now... The loop is in fact all I needed...
You can use loop. When the shuffled letters are the same as the original, recur back up to the start of the loop:
(defn shuffle-letters [word]
(let [letters (clojure.string/split word #"")]
(loop [] ; Start a loop
(let [shuffled-letters (shuffle letters)]
(if (= shuffled-letters letters) ; Check if they're equal
(recur) ; If they're equal, loop and try again
(clojure.string/join "" shuffled-letters)))))) ; Else, return the joined letters
There's many ways this could be written, but this is I think as plain as it gets. You could also get rid of the loop and make shuffle-letters itself recursive. This would lead to unnecessary work though. You could also use let-fn to create a local recursive function, but at that point, loop would likely be cleaner.
Things to note though:
Obviously, if you try to shuffle something like "H" or "HH", it will get stuck and loop forever since no amount of shuffling will cause them to differ. You could do a check ahead of time, or add a parameter to loop that limits how many times it tries.
This will actually make your shuffle less random. If you disallow it from returning the original string, you're reducing the amount of possible outputs.
The call to split is unnecessary. You can just call vec on the string:
(defn shuffle-letters [word]
(let [letters (vec word)]
(loop []
(let [shuffled-letters (shuffle letters)]
(if (= shuffled-letters letters)
(recur)
(clojure.string/join "" shuffled-letters))))))
Here's another solution (using transducers):
(defn shuffle-strict [s]
(let [letters (seq s)
xform (comp (map clojure.string/join)
(filter (fn[v] (not= v s))))]
(when (> (count (into #{} letters)) 1)
(first (eduction xform (iterate shuffle letters))))))
(for [_ (range 20)]
(shuffle-strict "test"))
;; => ("etts" "etts" "stte" "etts" "sett" "tste" "tste" "sett" "ttse" "sett" "ttse" "tset" "stte" "ttes" "ttes" "stte" "stte" "etts" "estt" "stet")
(shuffle-strict "t")
;; => nil
(shuffle-strict "ttttt")
;; => nil
We basically create a lazy list of possible shuffles, and then we take the first of them to be different from the input. We also make sure that there are at least 2 different characters in the input, so as not to hang (we return nil here since you don't want to have the input string as a possible result).
If you want your function to return a sequence:
(defn my-shuffle [input]
(when (-> input set count (> 1))
(->> input
(iterate #(apply str (shuffle (seq %))))
(remove #(= input %)))))
(->> "abc" my-shuffle (take 5))
;; => ("acb" "cba" "bca" "acb" "cab")
(->> "bbb" my-shuffle (take 5))
;; => ()
I want to replace a character in a string using index. How to do that? Or is there any other way of accessing it?
Like almost everything commonly used in Clojure, strings are immutable, so you need to create a new string with the new character in place of the old at the desired location:
(defn replace-at [s idx replacement]
(str (subs s 0 idx) replacement (subs s (inc idx))))
> (replace-at "012345" 2 "x")
01x345
Strings are immutable, but StringBuilderss are not, so you could leverage that:
(defn set-char-at [^String s idx ch]
(str (doto (StringBuilder. s) (.setCharAt idx ch))))
(set-char-at "foobar" 2 \x) ;;=> "foxbar"
I have started learning clojure. I am stuck at using let inside ->> macro
The code is :
(defn make-summary [wordStr]
;// split string into words
(let [words (clojure.string/split wordStr #"[\[\]\(\),.\s+]")
;// convert words to lowercase.
lowerCaseWords (map clojure.string/lower-case words)]
;// remove stop words
(->> (remove-stop-words lowerCaseWords)
;// count the frequency of words
;// ---------- HERE IS THE PROBLEM ------------------------------
(let [totalWords (count )] ;// <--- HOW TO MAKE MACRO PUT THE THING HERE ???
(count-frequency)
;// sort on the basis of frequency
(sort #(> (get %1 1) (get %2 1)))
;// find the keywords
)
)))
I am stuck at the second let inside the defn function.
How can i code it ?
You can use your original code with the as-> threading macro added in clojure 1.5
instead of inserting its argument to the first (->) or to the last (->>) position of each form, it lets you specify the position:
(as-> [1 2 3] x
(conj x 4)
(map inc x)) ;=> '(2 3 4 5)
I think, the advice is to use -> or ->> when you can and only fall back to as-> if this is not easily done. The summarise function by #Thumbnail is a nice example of the readability you get with ->>.
You can think of as-> as a convenient shorthand for the following code:
(let [x [1 2 3]
x (conj x 4)
x (map inc x)] x)
Here is the relevant part of your code written with as->:
(as-> (remove-stop-words lowerCaseWords) x
(let [totalWords (count x)] .....
Following #DiegoBasch's advice ...
If you're looking for
the words in decreasing order of frequency of use
eliminating stop words and
exploiting the ->> macro
then the following might suit:
(defn summarise [text stop-words]
(->> text
(re-seq #"[a-zA-Z]+")
(map clojure.string/lower-case)
(remove stop-words)
frequencies
(sort-by (comp - val))
(map key)))
For examples
(summarise "Mary had a HUGE a lamb" #{})
("a" "mary" "had" "huge" "lamb")
(summarise "Mary had a HUGE a lamb" #{"huge"})
("a" "mary" "had" "lamb")
Notes
The function detects words as sequences of letters instead of
detecting specific separator characters. You can reverse this change
if you prefer.
I'd be inclined to make sure that the stop words too are lower case:
use (set (map clojure.string/lower-case stop-words)) instead of
stop-words in the remove. Otherwise stop words with upper case
letters will be ineffective.
It is not possible to have ->> insert the argument at any place other than last item of the form. But this can be used in a trick to make it happen :
(defn make-summary [wordStr]
;// split string into words
(let [words (clojure.string/split wordStr #"[\[\]\(\),.\s+]")
;// convert words to lowercase.
lowerCaseWords (map clojure.string/lower-case words)]
;// remove stop words
(->> (remove-stop-words lowerCaseWords)
;// count the frequency of words
(fn [LCW] (let [totalWords (count LCW)] ;// <--- HOW TO MAKE MACRO PUT THE THING HERE ???
(count-frequency)
;// sort on the basis of frequency
(sort #(> (get %1 1) (get %2 1)))
;// find the keywords
)
))))
What i have done is wrapped the things inside the function, and now you can put the argument at any place.
I have a string that must be truncated at 200 characters if it is too long.
Checking the cheatsheet, (subs "Lorem Ipsum" 0 200) would seem to be an obvious choice, but it throws an exception if the second operator is greater than the length of the string.
Is there a simple, built-in function for truncating a string in Clojure? What's the simplest way to do this if I have to define my own?
You can check the length beforehand or use min to determine the actual number of characters that will remain:
(defn trunc
[s n]
(subs s 0 (min (count s) n)))
You can treat them as sequences and get safety (and elegance?) but the cost is performance:
(defn truncate
[s n]
(apply str (take n s)))