doseq Clojure returning nil, not accessing functions - clojure

What I'm trying to do is for every word in the sequence run each word through the function called transform, which will sort alphabetically and also change to lowercase. But all I am getting back is nil??
I'm guessing I'm using doseq wrong but it looks fine? Can anyone give me some pointers?
(defn sort-string [s]
(apply str (sort s)))
(defn transform [word x]
(let [x (sort-string (str/lower-case word))]
(prn word)
(prn word)))
(doseq [dictionary '("one" "two" "three" "FouR" "wot" "Rheet" "nope" "#")]
(transform dictionary))

doseq is for doing side effects while iterating over a sequence of items. For example, put each item one-by-one on a queue:
(doseq [msg messages]
(put-to-queue! msg))
It returns nil because it is intended to be used for side effects, not to compute some value.
To transform a list of values (which is what you are trying to do), you can use for, which has a similar syntax to doseq. You can also use map, filter, sort, etc.

doseq is only for side effects, you can use for instead if you want the results as a sequence, its syntax is the same as doseq.
(for [wordset '("one" "two" "three" "FouR" "wot" "Rheet" "nope" "#")]
(transform wordset))
=> ("eno" "otw" "eehrt" "foru" "otw" "eehrt" "enop" "#")

Here is an example :
(def words ["one" "two" "three" "FouR" "wot" "Rheet" "nope" "#"])
(sort (map clojure.string/lower-case words))
;; => ("#" "four" "nope" "one" "rheet" "three" "two" "wot")

There are problems with your transform function and your doseq expression. They suggest that you don't understand how information is passed around in a Clojure program:
Arguments to a function are passed by value, and are completely
unaffected by the evaluation.
A let binding redefines the name. It doesn't assign to the
existing one.
The only information that comes out of a function is its return value
(ignoring side-effects).
Your transform function
doesn't use its x argument
prints its word argument - twice - and returns nil.
And The let binding has no effect.
What you want is something like ...
(defn transform [word]
(sort-string (clojure.string/lower-case word)))
or, more concisely,
(def transform
(comp sort-string clojure.string/lower-case))
This returns the transformed string without the side-effect of printing it.
As others have explained, doseq is inappropriate. It always returns nil. Just ...
(let [dictionary '("one" "two" "three" "FouR" "wot" "Rheet" "nope" "#")]
(map transform dictionary))
... gives ...
("eno" "otw" "eehrt" "foru" "otw" "eehrt" "enop" "#")

Related

Write a function to print (non-negative) integer numbers in full words in Clojure

(defn num-as-words [n]
(let [words '("zero" "one" "two" "three" "four"
"five" "six" "seven" "eight" "nine")]
(clojure.string/join "-"
(map (fn [x] (nth words (Integer. (re-find #"\d" (str x)) ))) (str n)))))
I've written this function called as num-as-words which takes an integer and displays it as full words, for example if you were to input (123) it would return (one-two-three).
I've done it using a map but I was wondering if there was another way of doing it? I was also wondering if there was another way to connect the words rather than clojure.string/join, I was initially using interpose but didn't like the way it was outputting, as it looked like ("one" "-" "two" "-" "three").
Any help would be greatly appreciated, thank you.
user=> (clojure.pprint/cl-format ; formatted printing
nil ; ... to a string
"~{~R~^-~}" ; format (see below)
(map ; map over characters
(fn [x] (Integer. (str x))) ; convert char to integer
(str 123))) ; convert number to string
"one-two-three"
First, we take the input number, here hard-coded as "123" in the example, coerce it as a string and iterate over the resulting string's characters thanks to map. For each character, we build a string containing that character and parse it as an Integer. Thus, we obtain a list of digits.
More precisely, (fn [x] ...) is a function taking one argument. You should probably name it char instead (sorry), because we iterate over characters. When we evaluate (str x), we obtain a string containing one char, namely x. For example, if the character is \2, the resulting string is "2". The (Integer. string) form (notice the dot!) calls the constructor for the Integer class, which parse a string as an integer. To continue with our example, (Integer. "2") would yield the integer 2.
We use cl-format to print the list of digits into a fresh string (as requested by the false argument). In order to do that, we specify the format as follows:
~{...~} iterates over a list and executes the format inside the braces for each element.
~R prints a number as an english word (1 => one, etc.)
~^ escapes the iteration made by ~{...~} when there is no remaining arguments. So when we print the last digit, the part that follows ~^ is not printed.
What follows ~^ is simply the character -. This is used to separate strings but we had to take care to not print a dash for all iterations of the loop, otherwise the resulting string would have ended with a dash.
If any character cannot be parsed as an Integer then the function will report an error. You might want to check first that the input really is a positive integer before converting it to a string.
I'd implement it like this:
(defn num-as-words [n]
(let [words ["zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine"]]
(->> (str n)
(map #(Character/getNumericValue %))
(map words)
(clojure.string/join "-"))))
Using vector will simplify the implementation.
Instead of splitting number string with regular expression, you can treat it as sequence. In this case, you should use Charactor/getNumericValue to convert char to integer.
You can use ->> macro.
Using clojure.string/join looks fine.
interpose returns lazy sequence. That's why it returns like ("one" "-" "two"...). You should apply str to the result, (apply str (interpose ...)) to convert it to string.
If you want to handle negative numbers, you can modify the code like this:
(defn num-as-words [n]
(if (< n 0)
(str "-" (num-as-words (- n)))
(let [words ["zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine"]]
(->> (str n)
(map #(Character/getNumericValue %))
(map words)
(clojure.string/join "-")))))
This will prepend - in the front. If you just want to throw an error, you can use precondition:
(defn num-as-words [n]
{:pre [(<= 0 n)]}
(let [words ["zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine"]]
...
This will throw AssertionError when it receives negative number.

clojure using map to count the elements a vector of list

As you can see my first element is somehow being skipped... at least println thinks it is.
(def example [(:1 :1 :1) (:2 :2 :2 :2) (:3 :3)])
(println example)
(defn countEachSequence [vec]
(println vec)
(let [varName (count vec)]
(println varName)
)
)
(map #(countEachSequence %) example)
Desired output is:
([1 3] [:2 4] [:3 2])
This is naming the group and count the amount of elements in that group.
This seems what you want:
(defn count-each-seq [v]
(map (fn [s] [(first s) (count s)]) v))
The above count-each-seq function returns a sequence of vector. You can print it later.
If you run the function in REPL, the result will displayed immediately.
There are three things to say in your code:
When you write code, you should separate the core logic and presentation (in this case, println). And your function should return a value.
Your parameter vec is actually a function name clojure.core/vec. Pick other name that doesn't conflict with the existing function name.
Using dash between word is the clojure (or LISP) convention. So name like count-each-sequence instead of camel case.

Return the first non empty/blank value?

I have 2 bindings I'm calling path and callback.
What I am trying to do is to return the first non-empty one. In javascript it would look like this:
var final = path || callback || "";
How do I do this in clojure?
I was looking at the "some" function but I can't figure out how to combine the compjure.string/blank check in it. I currently have this as a test, which doesn't work. In this case, it should return nil I think.
(some (clojure.string/blank?) ["1" "2" "3"])
In this case, it should return 2
(some (clojure.string/blank?) ["" "2" "3"])
(first (filter (complement clojure.string/blank?) ["" "a" "b"]))
Edit: As pointed out in the comments, (filter (complement p) ...) can be rewritten as (remove p ...):
(first (remove clojure.string/blank? ["" "a" "b"]))
If you are so lucky to have "empty values" represented by nil and/or false you could use:
(or nil false "2" "3")
Which would return "2".
An equivalent to your JavaScript example would be:
(let [final (or path callback "")]
(println final))
If you want the first non blank string of a sequence you can use something like this:
(first (filter #(not (clojure.string/blank? %)) ["" "2" "3"]))
This will return 2
What i don't understand is your first example using the some function, you said that it should return nil but the first non blank string is "1".
This is how you would use the some function:
(some #(when-not (empty? %) %) ["" "foo" ""])
"foo"
(some #(when-not (empty? %) %) ["bar" "foo" ""])
"bar"
As others have pointed out, filter is another option:
(first (filter #(not (empty? %)) ["" "" "foo"])
"foo"
A third option would be to use recursion:
(defn first-non-empty [& x]
(let [[y & z] x]
(if (not (empty? y))
y
(when z (recur z)))))
(first-non-empty "" "bar" "")
"bar"
(first-non-empty "" "" "foo")
"foo"
(first-non-empty "" "" "")
nil
I used empty? instead of blank? to save on typing, but the only difference should be how whitespace is handled.
It was difficult for me to tell exactly what you wanted, so this is my understanding of what you are trying to do.
In my case, I wanted to find if an item in one report was missing in a second report. A match returned nil, and a non-match returned the actual item that did not match.
The following functions wind up comparing the value of a mapped value with a key.
Using something like find-first is probably what you want to do.
(defn find-first
"This is a helper function that uses filter, a comparision value, and
stops comparing once the first match is found. The actual match
is returned, and nil is returned if comparision value is not matched."
[pred col]
(first (filter pred col)))
(defn str-cmp
"Takes two strings and compares them. Returns 0 if a match; and nil if not."
[str-1 str-2 cmp-start-pos substr-len]
(let [computed-str-len (ret-lowest-str-len str-1 str-2 substr-len)
rc-1 (subs str-1 cmp-start-pos computed-str-len)
rc-2 (subs str-2 cmp-start-pos computed-str-len)]
(if (= 0 (compare rc-1 rc-2))
0
nil)))
(defn cmp-one-val
"Return nil if first key match found,
else the original comparision row is returned.
cmp-row is a single sequence of data from a map. i
cmp-key is the key to extract the comparision value.
cmp-seq-vals contain a sequence derived from
one key in a sequence of maps.
cmp-start and substr-len are start and stop
comparision indicies for str-cmp."
[cmp-row cmp-key cmp-seq-vals cmp-start substr-len]
(if (find-first #(str-cmp (cmp-key cmp-row) %1 cmp-start substr-len) cmp-seq-vals)
nil
cmp-row))

How to print a list as a string without parentheses

I did:
user=> (println (for [line (range 1 5)] (str "line=" line)))
and got:
(line=1 line=2 line=3 line=4)
but I wanted only line=1 line=2 line=3 line=4 as a string. How do I do this?
You need 'apply'.
(apply println (for [line (range 1 5)] (str "line=" line)))
Alternatively,
(println (apply str (interpose " " (map #(str "line=" %) (range 1 5)))))
What about this one. doseq is about doing side-effect on sequences and printing is a side-effect.
(doseq [line (range 1 5)
:let [msg (str "line=" line " ")]]
(print msg))
Instead of apply, you could alternatively use reduce like so:
user> (reduce #(str %1 " line=" %2) "" (range 1 5))
=> " line=1 line=2 line=3 line=4"
The reduce function is a function that takes a function (let's call if f), a "starting value", and then a list of things that will be used as the second argument to f. It lazily calls f on the starting value and the first item in the list, then calls f on what this returns and the second item in the list, then calls f on what this returns and the third item in the list etc., until it has exhausted all the items in the list (or rather--since it's lazy, it will only go through the whole list if you "ask it to").
If you don't like starting space, you could wrap the whole thing in triml (you'd have to do (use 'clojure.string) first). Or you could do (reduce #(str %1 "line=" %2 " ") (range 1 5)), which would put the space at the end.
My experience has been that anytime you can do something with apply, you can do it slightly more elegantly with reduce. More importantly, my reduce alternative has always usually been faster than my apply one. I certainly can't vouch that this will be true always, and I haven't done speed tests for your particular problem.
Edit
I did some rough timings, using my version (reduce) versus JohnJ's second suggestion (apply), and found that they were pretty similar for up to (range 1 100), but that by (range 1 500), the apply version was at least 4x faster.

clojure: for loop contents not invoked

I'm trying to build an XML structure using the internal data types from BaseX from Clojure.
(defn basex-elem [token-name dict]
(let [elem (org.basex.query.item.FElem.
(org.basex.query.item.QNm. token-name))]
(for [[k v] dict]
(do
(println "THIS IS REACHED")
(let [k-name (org.basex.query.item.QNm. (.getName k))
k-attr (org.basex.query.item.FAttr.
k-name
org.basex.util.Token/token v))]
(.add elem k-attr))))
elem))
When using this to cry to create an element, "THIS IS REACHED" is never printed:
(def test-elem (basex-elem "element-name" {:key1 "value1", :key2 "value2"}))
; => #'user/test-elem
And thus the value comes back without any attributes:
test-elem
; => #<FElem <element-name/>>
But adding attributes works otherwise.
(.add test-elem
(org.basex.query.item.FAttr.
(org.basex.query.item.QNm. "foo")
(org.basex.util.Token/token "bar")))
; => #<FElem <element-name foo="bar"/>>
Thus, presumably I'm doing something wrong with the loop. Any pointers?
for is not a loop construct in clojure, rather it's a list comprehension and produces a lazy sequence.
Use doseq instead when side effects are intended.