How to remove element from mutable vector? - vector

I know how to create mutable vector:
(defn create-vector []
(intern *ns* (symbol "my-vector" (ref []))
=>my-namespace/my-vector
I know how to add element to that vector:
(dosync (alter my-vector conj "test"))
=>["test"]
Now I have "test" string in my mutable vector. But how I can remove it? I tried to use lazy-sequence function remove
(dosync (alter my-vector remove "test"))
but it doesn't work. How can I remove element from mutable vector?
EDIT:
The collection doesn't really have to be vector. I found solution for set, but will wait if someone will suggest solution for vector.

your mistake is that you pass the "test" arg to remove function, while it really accepts a predicate. In your case this one wold work (unproperly though, making a list instead of a vector):
(dosync (alter my-vector #(remove #{"test"} %)))
to keep it a vector you would have to do this:
(dosync (alter my-vector #(vec (remove #{"test"} %))))
what makes me curious about your example:
why would you use this weird construction:
(defn create-vector []
(intern *ns* (symbol "my-vector" (ref []))))
instead of just (def my-vector (ref []))
why do you use ref at all? would you be using it inside some transaction? if not, i would propose you to move to an atom (since it is also thread safe)
user> (def my-atom (atom []))
#'user/my-atom
user> (swap! my-atom conj "hello")
["hello"]
user> (swap! my-atom #(remove #{"hello"} %))
()

As specified in my edit, collection doesn't have to be really vector. #leetwinski posted answer how to do it for vector, but if I would decide to use set I found other solution. So if we create set the same way as in question replacing [] to #{}:
(defn create-vector []
(intern *ns* (symbol "my-set" (ref #{}))
=>my-namespace/my-vector
and add element:
(dosync (alter my-set conj "test"))
=>#{"test"}
, we can remove it
(dosync (alter my-set disj item))
=>#{}

Related

Recur not at tail position

How can I use something similiar to recurnot at tail position?
Take a look at my code:
(defn -main [& args]
(println "Hi! Type a file name...")
(defn readFile[])
(let [fileName(read-line)]
(let [rdr (reader fileName)]
(if-not (.exists rdr)
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))
(defn list '())
(doseq [line (line-seq rdr)]
(if-not (= "" line)
(concat list '(line)))
(list))))
(defn fileLinesList (readFile))
...
...)
I know I can't use recur here... But I neither know how can I make it in clojure.
I'm a newbie in Clojure and I'm coming from a OOP context. So...
Is there a way to use recursion in this case?
What would be an alternative?
First of all you should not nest your functions definitions in another defn (-main in this case). defn or def always defines symbol bindings at the top level of namespace and they don't nest. If you want to define a locally scoped function you need to use let and fn, e.g.
(let [my-fn (fn [a b] (+ a b))]
(my-fn 1 2))
In your particular case I think it would be easier to split your code into multiple functions. This way it will be more readable.
Prompting for a file name is one piece of your logic.
(defn get-existing-filename []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
filename
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Then you can use it to read a file removing empty lines:
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
For a file with following content:
AAA
BBB
CCC
DDD
it will return
("AAA" "BBB" "CCC" "DDD")
If you really want it as a single function, the following will work:
(defn read-file []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Finally, this function can be called from -main.
I have also noticed another issue in your sample code:
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
if and if-not require a single expression for their then and else branches. If you want to have multiple expressions you need to nest them in do:
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
If you need if or if-not without the else branch then you can use when or when-not macros. Then you don't need to wrap multiple expressions because when/when-not will wrap them for your inside of do.
(when true
(println 1)
(println 2))
is equivalent to
(if true
(do
(println 1)
(println 2)))

Adding "not" to each item in a sequence in Clojure

I am trying to add (not(X)) to all my items X in a sequence.
For example:
Convert (a b) to
( (not(a)) (not(b)) )
When I use (map (fn [x] (not(x))) mylist), it tries to evaluate the nots and return booleans.
When I use (map (fn [x] '(not(x))) mylist), it just returns a list of (not(x)) without actually putting in my list's variables.
(a b) --> ( (not(a)) (not(b)) ) ? Thanks!
user=> (map (fn [x] (list 'not (list x))) '(a b))
((not (a)) (not (b)))
The ' single quote operator is convenient for making lists because it prevents evaluation, but it isn't usable in your case because you have content inside the resulting list that you want to be evaluated.
Another option would have been ` AKA quasiquote, which allows selective unquoting, but also namespaces symbols (once again, not useful in your case, where you want the symbol used literally).
You can make it more readable and get rid of numerous list calls
by using syntax-quote reader macro:
user> (map (fn [x] `(~'not (~x))) '(a b))
((not (a)) (not (b)))
(see clojure reader documentation's section on [syntax quoting](
http://clojure.org/reader))
unquote-quote not (~'not) is used here to insert literal not symbol instead of namespace-prefixed clojure.core/not

Simple Calculator (Addition function input) in Clojure

I'm trying to write a simple calculator with addition, subtraction, etc.
My problem is with getting the user input. How do I turn the string of numerical values into a vector? And also what is a better way to write the program?
(ns scalc.core)
(defn add
[numbers]
(println (apply + numbers)))
(defn numchoose
[]
(println "What numbers?: ")
(let [numbers (read-line)] numbers))
(defn opchoose
[]
(println "What operation would you like to do?: ")
(let [operation (read-line)]
(if (= operation "add")
(do
(println "You chose to add.")
(let [numvect (numchoose)]
(add [numvect]))))))
(defn -main
[& args]
(opchoose)
(numchoose))
And this is the error:
~/clj/scalc 1/7 % lein trampoline run -m scalc.core
What operation would you like to do?:
add
You chose to add.
What numbers?:
5 7
Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.String to java.lang.Number
at java.lang.Class.cast(Class.java:3005)
at clojure.core$cast.invoke(core.clj:318)
at clojure.core$_PLUS_.invoke(core.clj:927)
at clojure.lang.AFn.applyToHelper(AFn.java:161)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at clojure.core$apply.invoke(core.clj:601)
at scalc.core$add.invoke(core.clj:5)
at scalc.core$opchoose.invoke(core.clj:21)
at scalc.core$_main.doInvoke(core.clj:27)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.Var.invoke(Var.java:411)
at user$eval15.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6511)
at clojure.lang.Compiler.eval(Compiler.java:6501)
at clojure.lang.Compiler.eval(Compiler.java:6477)
at clojure.core$eval.invoke(core.clj:2797)
at clojure.main$eval_opt.invoke(main.clj:297)
at clojure.main$initialize.invoke(main.clj:316)
at clojure.main$null_opt.invoke(main.clj:349)
at clojure.main$main.doInvoke(main.clj:427)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:419)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)
EDIT: the solved program now looks like this:
(ns scalc.core)
(defn add [numbers]
(reduce + numbers))
(defn numchoose []
(let [nums (re-seq #"\d+" (read-line))]
(map #(Integer/parseInt %) nums)))
(defn delegate []
(println "What operation would you like to do?: ")
(let [operation (read-line)]
(when (= operation "add")
(println "You chose to add.")
(println "What numbers? ")
(add (numchoose)))))
(defn -main
[& args]
(delegate))
For getting the numbers, you can use re-seq:
(re-seq #"\d+" "123 456 789") => ("123" "456" 789")
You still only have strings rather than numbers though. You can use read-string to get the numbers (read-string is convenient, but not safe in all cases. Here we make sure there are really only numbers in these strings so it's fine).
(read-string "5") => 5
Instead of (apply + numbers) you could use reduce: (reduce + numbers), also your add function really shouldn't print anything (you should try to separate functional functions from side-effecty functions whenever possible).
This (let [numbers (read-line)] numbers) is equal to (read-line). Don't overcomplicate things!
Instead of
(if (= operation "add")
(do ... ))
you can write
(when (= operation "add")
...)
when is just a macro that's useful when you don't need the else case in your ifs (it wraps everything after the condition in a do, and evaluates to nil when the condition evaluates to false).

Clojure recursive function

As a Clojure newbie, I'm bothered with this small problem:
I would like to iterate through a sequence and execute a split, and then a str (concatenation) function over the sequence elements.
Here is my sequence:
(("2.660.784") ("2.944.552") ("44.858.797"))
What I want to get is something like this:
("2660784" "2944552" "44858797")
And this is my attempt of creating recursive solution for my problem:
(defn old
[squence]
(let [size (count squence)]
(loop [counter 1]
(if (<= counter size)
(apply str (clojure.string/split
(first (first squence))
#"\b\.\b"
))
(old (rest squence)))
)))
And of course, this is not a solution because it applies split and str only to one element, but I would like to repeat this for each element in squence. The squence is product of some other function in my project.
I'm definitely missing something so please help me out with this one...
The simplest way to write it is with replace, rather than split/str. And once you've written a function that can do this transformation on a single string, you can use map or for to do it to a sequence of strings. Here I had to destructure a bit, since for whatever reason each element of your sequence is itself another sequence; I just pulled out the first element.
(for [[s] '(("2.660.784") ("2.944.552") ("44.858.797"))]
(clojure.string/replace s #"\b\.\b" ""))
user=> (defn reject-char-from-string
[ch sequence]
(map #(apply str (replace {ch nil} (first %))) sequence))
#'user/reject-char-from-string
user=> (reject-char-from-string \. '(("2.660.784") ("2.944.552") ("44.858.797"))
)
("2660784" "2944552" "44858797")
Tried this?
=> (flatten '(("2.660.784") ("2.944.552") ("44.858.797")))
("2.660.784" "2.944.552" "44.858.797")
Is it as simple as this?
(def data '(("2.660.784") ("2.944.552") ("44.858.797")))
(require '[clojure.string :as string])
(map #(string/replace (first %1) "." "") data)
;=> ("2660784" "2944552" "44858797")

"Unable to resolve symbol" error

When I paste this code into a REPL, it works fine:
(use 'clojure.contrib.seq-utils)
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
Now, I have this code with :gen-class :implements [my.ServiceInterface] and a function prefixed with - to implement the interface. I unit-test with Maven/Groovy/TestNG. Everything works fine with several other interfaces/Clojure implementations, but in this particular case, I get this error:
java.lang.RuntimeException:
java.lang.Exception: Unable to resolve symbol: random-letter in this context (NO_SOURCE_FILE:32)
I can't figure out why. The only thing I can tell is different in this function from all the other functions, is that this is the only place where I use quoting, i.e. '((random-digit)) and '(random-letter). EDIT: also, this is the only place where I use eval.
I tried defining the functions as "non-private" (defn instead of defn-). I also tried a (declare random-digit random-letter) at the top. Neither of these solves the problem.
On a side note, if you have a suggestion for a better way to implement the random-password function, I am all ears. But I'd still like to know why I am getting this error and how to get this to work.
Many thanks in advance for your help. Clojure is awesome.
Edit: here is the complete code.
(ns fred.hp2010.service.ClojurePoolerService
(:gen-class :implements [fred.hp2010.service.PoolerService])
(:use [clojure.contrib.seq-utils :only (flatten)]))
(def dao (fred.hp2010.persistence.Repository/getDao))
(declare find-by is-taken random-password)
(defn -addPooler [this pooler] (. dao insert "POOLER" pooler))
(defn -getPoolers [this] (. dao list "poolers"))
(defn -isEmailTaken [this email] (is-taken {"email" email}))
(defn -isUsernameTaken [this username] (is-taken {"username" username}))
(defn -login [this email password] (. dao findSingle "POOLER" {"email" email "password" password}))
(defn -changePassword [this email new-password]
(let [updated-pooler (assoc (into {} (find-by {"email" email})) "password" new-password)]
(. dao update "POOLER" "POOLER_ID" updated-pooler)))
(defn -resetPassword [this email]
(let [new-password (random-password)]
(-changePassword this email new-password)
new-password))
(defn- find-by [params] (. dao findSingle "POOLER" params))
(defn- is-taken [params] (not (nil? (find-by params))))
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
I don't know why you're having problems compiling this with :gen-class but I wouldn't be surprised if eval had something to do with it. eval is usually a bad idea. One thing to try (completely untested) is to use ` (backquote) instead of ' (quote) so that your symbols are namespace-qualified. Don't know if that'd help or not.
Probably better to get rid of eval though. If you turn your random-character functions into infinite lazy seqs via repeatedly you can do it this way:
(defn- random-letter [] (repeatedly #(char (+ (rand-int 26) 97))))
(defn- random-digit [] (repeatedly #(rand-int 10)))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(apply str
(mapcat (fn [[n f]] (take n (f)))
[[2 random-letter]
[1 random-digit]
[2 random-letter]
[1 random-digit]
[2 random-letter]])))
In the top handful of lines, I'm having a bit of trouble following the syntax. In particular, why all the quotes in line 7? Delayed evaluation of all those expressions is probably not helping you. I would guess that the quoted '(random-letter) is spoiling your fun.
You can probably write simpler code while eschewing eval. I'm going to go try it in the REPL, I hope to be back soon with an improved version.
EDIT:
OK, this works:
(apply str (interpose (random-digit) (repeat 3 (apply str (repeat 2 (random-letter))))))
...and it doesn't need anything from clojure.contrib :)
The str function will mung any arguments together into a string. If the arguments are in a list, you smuggle str inside the list by using apply.
As you said, Clojure is cool!
EDIT:
Here's a function that generates a random string of alphas and numerics in accordance with a String specification:
(apply str (map (fn [c] (if (= c \a) (random-letter) (random-digit))) "aanaanaa")))
It deviates a little bit from your spec but I think it's pretty cool.

Resources