I want to implement a clojure function that will return the substring in a given string,
Given a string
(def mystring "clojurestring")
(subs mystring 0 3) ;=> "clo"
I want to be able to use a negative index to get sub-string starting from the last character i.e
(subs mystring -13 3) ;=> "clo"
I also think
(subs mystring -9 7) ;=> "ure"
The format for getting the substring in clojure is
(subs 'string' start end)
start. Starting offset, negative offset counts from end of string.Parameter is a positive or negative number.
end. Parameter is an integer greater than zero
so far I have tried without any luck, I don't know if this is possible
Any suggestions? Thanks!
If start is negative, you simply want to subtract from the length of the string. If I understand your requirement correctly:
(defn subs* [s start end]
(if (neg? start)
(subs s (+ (.length s) start) end)
(subs s start end)))
(defn my-subs [st from end]
(let [f (if (neg? from)
(+ (count st) from)
from)]
(subs st f end)))
(my-subs "clojurestring" -9 7)
;"ure"
Related
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"
(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.
I'm trying to split a string into n chunks of variable sizes.
As input I have a seq of the sizes of the different chunks:
(10 6 12)
And a string:
"firstchunksecondthirdandlast"
I would like to split the string using the sizes as so:
("firstchunk" "second" "thirdandlast")
As a newbie I still have a hard time wrapping my head around the most idiomatic way to do this.
Here is two ways to do this:
One version uses reduce which you can use very often if you want to carry some kind of state (here: The index where you're currently at). The reduce would need a second fn call applied to it to have the result in your form.
;; Simply take second as a result:
(let [s "firstchunksecondthirdandlast"]
(reduce
(fn [[s xs] len]
[(subs s len)
(conj xs (subs s 0 len))])
[s []]
[10 6 12]))
The other version first builds up the indices of start-end and then uses destructing to get them out of the sequence:
(let [s "firstchunksecondthirdandlast"]
(mapv
(fn [[start end]]
(subs s start end))
;; Build up the start-end indices:
(partition 2 1 (reductions + (cons 0 [10 6 12])))))
Note that neither of these are robust and throw ugly errors if the string it too short. So you should be much more defensive and use some asserts.
Here is my go at the problem (still a beginner with the language), it uses an anonymous function and recursion until the chunks list is empty. I have found this pattern useful when wanting to accumulate results until a condition is met.
str-orig chunks-orig [] sets the initial arguments for the anonymous function: the full string, full list of chunks and an empty vec to collect results into.
(defn split-chunks [str-orig chunks-orig]
((fn [str chunks result]
(if-let [len (first chunks)] (recur
(subs str len)
(rest chunks)
(conj result (subs str 0 len)))
result))
str-orig chunks-orig []))
(split-chunks "firstchunksecondthirdandlast" '(10 6 12))
; ["firstchunk" "second" "thirdandlast"]
Trying out this snippet of code and doesn't seem to be working quite right..
(defn- multiple_of?
[div num]
(= (mod num div) 0))
(defn sum_of_multiples_from
([start] (sum_of_multiples_from start 0))
([start total]
(if (<= start 0)
total
(recur (dec start) (or (multiple_of? 3 start) (multiple_of? 5 start)
(+ total start) start)))))
I receive the following error:
java.lang.Boolean cannot be cast to java.lang.Number
I am guessing it has to do with:
(recur (dec start) (or (multiple_of? 3 start) (multiple_of? 5 start)
(+ total start)
start)))))
But I'm not sure why, I'm new to clojure, so I'm trying to get a grasp of recur.
You really want a conditional expression for your recur, and one that always returns a number. For example:
(defn sum_of_multiples_from
([start] (sum_of_multiples_from start 0))
([start total]
(if (<= start 0)
total
(recur (dec start)
(if (or (multiple_of? 3 start) (multiple_of? 5 start))
(+ total start)
total)))))
Note that this is a weird way to implement this in a functional language. You're really picking values from a range and adding them up, so it's better to implement this as a filter and a reduce, for example:
(reduce + (filter #(or (multiple_of? 3 %) (multiple_of? 5 %)) (range 50)))
=> 543
Your or call returns a boolean ((multiple_of? 3 start)) as soon as start is a multiple of 3.
In Clojure, or always returns one of its arguments -- either the first truish one if one exists, or the last falsish one otherwise.
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)))