The aim is to play with a slight modification of the Caesar cipher.
First a function to move a character:
(defn move-char [c shift idx encode-or-decode]
(let [ch (int c) val (mod (* encode-or-decode (+ shift idx)) 26)]
(cond
(and (>= ch (int \A)) (<= ch (int \Z))) (char (+ (mod (+ val (- (int ch) (int \A))) 26) (int \A)))
(and (>= ch (int \a)) (<= ch (int \z))) (char (+ (mod (+ val (- (int ch) (int \a))) 26) (int \a)))
:else c)))
Then a function to map the last one to a string:
(defn move-shift-aux [str shift encode-or-decode]
(map-indexed (fn [idx item] (move-char item shift idx encode-or-decode)) str))
`(move-shift-aux "I should have known..." 1 1)` returns
(\J \space \v \l \t \a \s \l \space \r \l \h \r \space \z \d \f \o \g \. \. \.)
and if I write:
(apply str (move-shift-aux "I should have known..." 1 1))
I get what I want:
"J vltasl rlhr zdfog..."
But if I define:
(defn moving-shift [str shift]
(apply str (move-shift-aux str shift 1)))
(moving-shift "I should have known..." 1)
I get:
CompilerException java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn, compiling:(caesar\core.clj:29:44)
I don't understand why the compiler exception while it does work fine when applying directly.
You're shadowing the str symbol from clojure.core with your str parameter. Inside moving-shift's scope, str refers to "I should have known..." and not clojure.core/str, hence when you call your apply function, you get a ClassCastException, stating that a string is not a function.
Use another name for your string parameter.
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
I'm trying to write a function that counts the number of vowels and consonants in a given string. The return value is a map with two keys, vowels and consonants. The values for each respective key are simply the counts.
The function that I have been able to develop so far is
(defn count-vowels-consenants [s]
(let [m (atom {"vowels" 0 "consenants" 0})
v #{"a" "e" "i" "o" "u"}]
(for [xs s]
(if
(contains? v (str xs))
(swap! m update-in ["vowels"] inc)
(swap! m update-in ["consenants"] inc)
))
#m))
however (count-vowels-consenants "sldkfjlskjwe") returns {"vowels":0 "consenants": 0}
What am I doing wrong?
EDIT: changed my input from str to s as str is a function in Clojure.
I think for is lazy so you're not going to actually do anything until you try to realize it. I added a first onto the for loop which realized the list and resulted in an error which you made by overwriting the str function with the str string. Ideally, you would just do this without the atom rigmarole.
(defn count-vowels-consonants [s]
(let [v #{\a \e \i \o \u}
vowels (filter v s)
consonants (remove v s)]
{:consonants (count consonants)
:vowels (count vowels)}))
if the atom is what you want, then use doseq instead of for and it will update the atom for everything in the string. also make sure you don't overwrite the str function by using it in your function binding.
if this side effecting scheme is inevitable (for sume educational reason, i suppose) just replace for with doseq which is a side effecting eager equivalent of for
(by the way: there is a mistake in your initial code: you use str as an input param name, and then try to use it as a function. So you are shadowing the def from the clojure.core, just try to avoid using params named like the core functions):
(defn count-vowels-consenants [input]
(let [m (atom {"vowels" 0 "consenants" 0})
v #{"a" "e" "i" "o" "u"}]
(doseq [s input]
(if (contains? v (str s))
(swap! m update-in ["vowels"] inc)
(swap! m update-in ["consenants"] inc)))
#m))
#'user/count-vowels-consenants
user> (count-vowels-consenants "asdfg")
;; {"vowels" 1, "consenants" 4}
otherwise you could do something like this:
user> (reduce #(update %1
(if (#{\a \e \i \o \u} %2)
"vowels" "consonants")
(fnil inc 0))
{} "qwertyui")
;;{"consonants" 5, "vowels" 3}
or
user> (frequencies (map #(if (#{\a \e \i \o \u} %)
"vowels" "consonants")
"qwertyui"))
;;{"consonants" 5, "vowels" 3}
or this (if you're good with having true/false instead of "vowels/consonants"):
user> (frequencies (map (comp some? #{\a \e \i \o \u}) "qwertyui"))
;;{false 5, true 3}
for is lazy as mentioned by #Brandon H. You can use loop recur if you want. Here I change for with loop-recur.
(defn count-vowels-consenants [input]
(let [m (atom {"vowels" 0 "consenants" 0})
v #{"a" "e" "i" "o" "u"}]
(loop [s input]
(when (> (count s) 0)
(if
(contains? v (first (str s) ))
(swap! m update-in ["vowels"] inc)
(swap! m update-in ["consenants"] inc)
))
(recur (apply str (rest s))))
#m))
The question, and every extant answer, assumes that every character is a vowel or a consonant: not so. And even in ASCII, there are lower and upper case letters. I'd do it as follows ...
(defn count-vowels-consonants [s]
(let [vowels #{\a \e \i \o \u
\A \E \I \O \U}
classify (fn [c]
(if (Character/isLetter c)
(if (vowels c) :vowel :consonant)))]
(map-v count (dissoc (group-by classify s) nil))))
... where map-v is a function that map's the values of a map:
(defn map-v [f m] (reduce (fn [a [k v]] (assoc a k (f v))) {} m))
For example,
(count-vowels-consonants "s2a Boo!")
;{:vowel 3, :consonant 2}
This traverses the string just once.
I am looking for a nice method to split a number with n digits in Clojure I have these two methods:
(->> (str 942)
seq
(map str)
(map read-string)) => (9 4 2)
and...
(defn digits [n]
(cons
(str (mod n 10)) (lazy-seq (positive-numbers (quot n 10)))))
(map read-string (reverse (take 5 (digits 10012)))) => (1 0 0 1 2)
Is there a more concise method for doing this type of operation?
A concise version of your first method is
(defn digits [n]
(->> n str (map (comp read-string str))))
... and of your second is
(defn digits [n]
(if (pos? n)
(conj (digits (quot n 10)) (mod n 10) )
[]))
An idiomatic alternative
(defn digits [n]
(->> n
(iterate #(quot % 10))
(take-while pos?)
(mapv #(mod % 10))
rseq))
For example,
(map digits [0 942 -3])
;(nil (9 4 2) nil)
The computation is essentially eager, since the last digit in is the
first out. So we might as well use mapv and rseq (instead of map and reverse) to do it faster.
The function is transducer-ready.
It works properly only on positive numbers.
You could simply do
(map #(Character/digit % 10) (str 942))
EDIT: Adding a function definition
(defn digits [number] (map #(Character/digit % 10) (str number)))
Usage:
(digits 1234)
Note: This is concise, but does use java String and Character classes. An efficient implementation can be written using integer modulo arithmetic, but won't be concise. One such solution similar to Charles' answer would be:
(defn numTodigits
[num]
(loop [n num res []]
(if (zero? n)
res
(recur (quot n 10) (cons (mod n 10) res)))))
Source
I'm not sure about concise, but this one avoids unnecessary inefficiency such as converting to strings and back to integers.
(defn digits [n]
(loop [result (list), n n]
(if (pos? n)
(recur (conj result (rem n 10))
(quot n 10))
result)))
A recursive implementation (could be more efficient and less concise, but it shouldn't matter for reasonable numbers).
(defn digits [n]
(when (pos? n)
(concat (digits (quot n 10))
[(mod n 10)])))
a looping method:
(defn split-numbers [number]
(loop [itr 0 res [] n number]
(if (= n 0)
res
(recur (inc itr) (concat (vector (mod n 10)) res) (int (/ n 10)))
)
)
)
Easiest i could find:
(->> (str n)
seq
(map (comp read-string str)))
Is there an idiomatic way of encoding and decoding a string in Clojure as hexadecimal? Example from Python:
'Clojure'.encode('hex')
# ⇒ '436c6f6a757265'
'436c6f6a757265'.decode('hex')
# ⇒ 'Clojure'
To show some effort on my part:
(defn hexify [s]
(apply str
(map #(format "%02x" (int %)) s)))
(defn unhexify [hex]
(apply str
(map
(fn [[x y]] (char (Integer/parseInt (str x y) 16)))
(partition 2 hex))))
(hexify "Clojure")
;; ⇒ "436c6f6a757265"
(unhexify "436c6f6a757265")
;; ⇒ "Clojure"
Since all posted solutions have some flaws, I'm sharing my own:
(defn hexify "Convert byte sequence to hex string" [coll]
(let [hex [\0 \1 \2 \3 \4 \5 \6 \7 \8 \9 \a \b \c \d \e \f]]
(letfn [(hexify-byte [b]
(let [v (bit-and b 0xFF)]
[(hex (bit-shift-right v 4)) (hex (bit-and v 0x0F))]))]
(apply str (mapcat hexify-byte coll)))))
(defn hexify-str [s]
(hexify (.getBytes s)))
and
(defn unhexify "Convert hex string to byte sequence" [s]
(letfn [(unhexify-2 [c1 c2]
(unchecked-byte
(+ (bit-shift-left (Character/digit c1 16) 4)
(Character/digit c2 16))))]
(map #(apply unhexify-2 %) (partition 2 s))))
(defn unhexify-str [s]
(apply str (map char (unhexify s))))
Pros:
High performance
Generic byte stream <--> string conversions with specialized wrappers
Handling leading zero in hex result
Your implementation(s) don't work for non-ascii characters,
(defn hexify [s]
(apply str
(map #(format "%02x" (int %)) s)))
(defn unhexify [hex]
(apply str
(map
(fn [[x y]] (char (Integer/parseInt (str x y) 16)))
(partition 2 hex))))
(= "\u2195" (unhexify(hexify "\u2195")))
false ; should be true
To overcome this you need to serialize the bytes of the string using the required character encoding, which can be multi-byte per character.
There are a few 'issues' with this.
Remember that all numeric types are signed in the JVM.
There is no unsigned-byte.
In idiomatic java you would use the low byte of an integer and mask it like this wherever you used it.
int intValue = 0x80;
byte byteValue = (byte)(intValue & 0xff); -- use only low byte
System.out.println("int:\t" + intValue);
System.out.println("byte:\t" + byteValue);
-- output:
-- int: 128
-- byte: -128
clojure has (unchecked-byte) to effectively do the same.
For example, using UTF-8 you can do this:
(defn hexify [s]
(apply str (map #(format "%02x" %) (.getBytes s "UTF-8"))))
(defn unhexify [s]
(let [bytes (into-array Byte/TYPE
(map (fn [[x y]]
(unchecked-byte (Integer/parseInt (str x y) 16)))
(partition 2 s)))]
(String. bytes "UTF-8")))
; with the above implementation:
;=> (hexify "\u2195")
"e28695"
;=> (unhexify "e28695")
"↕"
;=> (= "\u2195" (unhexify (hexify "\u2195")))
true
Sadly the "idiom" appears to be using the Apache Commons Codec, e.g. as done in buddy:
(ns name-of-ns
(:import org.apache.commons.codec.binary.Hex))
(defn str->bytes
"Convert string to byte array."
([^String s]
(str->bytes s "UTF-8"))
([^String s, ^String encoding]
(.getBytes s encoding)))
(defn bytes->str
"Convert byte array to String."
([^bytes data]
(bytes->str data "UTF-8"))
([^bytes data, ^String encoding]
(String. data encoding)))
(defn bytes->hex
"Convert a byte array to hex encoded string."
[^bytes data]
(Hex/encodeHexString data))
(defn hex->bytes
"Convert hexadecimal encoded string to bytes array."
[^String data]
(Hex/decodeHex (.toCharArray data)))
I believe your unhexify function is as idiomatic as it can be. However, hexify can be written in a simpler way:
(defn hexify [s]
(format "%x" (new java.math.BigInteger (.getBytes s))))
user=> (char 65)
\A
user=> (char 97)
\a
user=> (str (char 65))
"A"
user=> (str (char 97))
"a"
These are the characters from the ascii decimal values ...
How do I get the ascii decimal values from the characters?
A character is a number, it's just that clojure is showing it to you as a char. The easiest way is to just cast that char to an int.
e.g.
user=> (int \A)
65
user=> (int (.charAt "A" 0))
65
user=> (doseq [c "aA"] (printf "%d%n" (int c)))
97
65
nil
user=> (map int "aA");;
(97 65)
user=> (apply str (map char [97 65]))
"aA"