take-while on clojure.string does not work - clojure

I'm wondering why clojure does not treat string as an array like in scala or haskell.
I want take-while function on string as in scala below
scala> "chich and chong".takeWhile(_ != ' ')
res1: String = chich
But take-while in clojure does not seem to work with string.
user=> (take-while #(not= % " ") "chich and chong")
(\c \h \i \c \h \space \a \n \d \space \c \h \o \n \g)
Just to make sure char/string equality works in clojure,
user=> (= " " " ")
true
user=> (not= 'A " ")
true
take-while does work with vector only.
user=> (take-while #(< % 0) [-3 -2 -1 0 1 2 3])
(-3 -2 -1)
Tried converting string to vector as well, but returns the same as input.
user=> (vec "apple")
[\a \p \p \l \e]
user=> (take-while #(not= % "p") (vec "apple"))
(\a \p \p \l \e)
how can I use take-while with clojure.string?

You should write character literal instead of string with space:
user=> (take-while #(not= % \space) "chich and chong")
=> (\c \h \i \c \h)
That is because:
" " - is java.lang.String
\space - is java.lang.Character
more info \ - Character literal

Just to point out that your code would work in ClojuseScript, because the host platform (JavaScript) has no character type, so characters are represented as one-character strings. On the JVM though characters are there own type.

Related

How to get all trigrams of a string in clojure

Suppose I have a string "This is a string". The tri-grams would be "Thi", "his", "is ", "s i" etc. I want to return a vector of all the trim-grams. How can I do that?
You can use partition or partition-all depending on whether you are
interested also in the last "non-tri-grams":
user=> (doc partition)
-------------------------
clojure.core/partition
([n coll] [n step coll] [n step pad coll])
Returns a lazy sequence of lists of n items each, at offsets step
apart. If step is not supplied, defaults to n, i.e. the partitions
do not overlap. If a pad collection is supplied, use its elements as
necessary to complete last partition upto n items. In case there are
not enough padding elements, return a partition with less than n items.
(user=> (doc partition-all)
-------------------------
clojure.core/partition-all
([n] [n coll] [n step coll])
Returns a lazy sequence of lists like partition, but may include
partitions with fewer than n items at the end. Returns a stateful
transducer when no collection is provided.
E.g.
user=> (partition 3 1 "This is a string")
((\T \h \i)
(\h \i \s)
(\i \s \space)
(\s \space \i)
(\space \i \s)
(\i \s \space)
(\s \space \a)
(\space \a \space)
(\a \space \s)
(\space \s \t)
(\s \t \r)
(\t \r \i)
(\r \i \n)
(\i \n \g))
To get the strings back, join the chars:
user=> (map clojure.string/join (partition 3 1 "This is a string"))
("Thi"
"his"
"is "
"s i"
" is"
"is "
"s a"
" a "
"a s"
" st"
"str"
"tri"
"rin"
"ing")
Or replace with partition-all accordingly:
user=> (map clojure.string/join (partition-all 3 1 "This is a string"))
("Thi"
; ...
"rin"
"ing"
"ng" ; XXX
"g") ; XXX

Clojure: difference between applying directly or by way of a function

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.

How can I read strings with backslashes?

Apparently I can't read-string some strings, like
user> (read-string "\" \\ABC \"")
RuntimeException Unsupported escape character: \A clojure.lang.Util.runtimeException (Util.java:219)
user>
Is there a way around that?
Thanks!
I assume that you want to end up with a string that when you print its "\ABC", so:
user=> (println "\\ABC")
\ABC
nil
As you see, the reader needs two "\". As read-string expects the string to be a valid Clojure expression and from your example you are trying to read a string that contains a string, you need to escape both the " (as you are doing) and the two \ :
user=> (def s (read-string "\" \\\\AB\""))
#'user/s
user=> (class s)
java.lang.String
user=> (println s)
\AB
nil
user=> s
" \\AB"

How to convert a clojure string of numbers into separate integers?

I can read some data in like this in the repl. For a real program I plan to assign in a let special form.
(def x1 (line-seq (BufferedReader. (StringReader. x1))))
If I enter 5 5, x1 is bound to ("5 5")
I would like to convert this list of one element into a list of two integers. How can I do that? I have been playing around with parsing the string on whitespace, but am having trouble performing the conversion to integer.
Does this help? In Clojure 1.3.0:
(use ['clojure.string :only '(split)])
(defn str-to-ints
[string]
(map #(Integer/parseInt %)
(split string #" ")))
(str-to-ints "5 4")
; => (5 4)
(apply str-to-ints '("5 4"))
; => (5 4)
In case the Clojure version you're using doesn't have clojure.string namespace you can skip the use command and define the function in a following way.
(defn str-to-ints
[string]
(map #(Integer/parseInt %)
(.split #" " string)))
You can get rid of regular expressions by using (.split string " ") in the last line.
Works for all numbers and returns nil in the case it isn't a number (so you can filter out nils in the resulting seq)
(require '[clojure.string :as string])
(defn parse-number
"Reads a number from a string. Returns nil if not a number."
[s]
(if (re-find #"^-?\d+\.?\d*$" s)
(read-string s)))
E.g.
(map parse-number (string/split "1 2 3 78 90 -12 0.078" #"\s+"))
; => (1 2 3 78 90 -12 0.078)
The string can be wrapped with brackets and after that evaluated as clojure list with read-string function:
(def f #(read-string (str "(" % ")")))
(f "5 4")
; => (5 4)

Closure's (apply str) questions?

With closure
(apply str [\a \b])
and
(apply str '(\a \b))
returns "ab".
(apply str (\a \b))
returns an error.
Why is that?
Because (\a \b) means "call the function \a with an argument of \b", and since the character \a is not a function, it fails. Note the difference in the following:
user=> (+ 1 2 3)
6
user=> '(+ 1 2 3)
(+ 1 2 3)
As a general rule, if you want to write a literal sequence, use a vector instead of a quoted list since the quote will also stop evaluation of the parts inside the list, e.g.:
user=> [(+ 1 2) (+ 3 4)]
[3 7]
user=> '((+ 1 2) (+ 3 4))
((+ 1 2) (+ 3 4))