I'm trying to learn Clojure and I'm starting with some basics such as functions, concatenation, lexicon replacement, etc. The one I'm stuck on currently is replacement. I've created a function called test that accepts two parameters v and v2; it's purpose is to replace periods with whitespace using clojure.string/replace:
(defn replace-lexicon [value lexicon replacementValue]
(println str(clojure.string/replace value lexicon replacementValue)))
(replace-lexicon "this.is.a.test" "." " ")
The expected output is:
this is a test
However, the output has additional information with it:
#object[clojure.core$str 0x35aea049 clojure.core$str#35aea049] this is a test
I tried searching for answers on how to remove it, but I'm not finding anything conclusive. Drawing on my experience with other languages, I can't help but feel this is demonstrating that what's being printed isn't a string but rather an object. Unfortunately though, this is just an educated guess, and, I haven't been able to prove it.
What is #object..., and how do I remove it to correct my output?
Close! The extra str is the problem. Here is a version written with unit tests from my favorite template project:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[clojure.string :as str]
))
(defn replace-lexicon
[value lexicon replacementValue]
(str/replace value lexicon replacementValue))
(dotest
(is= "this is a test" (replace-lexicon "this.is.a.test" "." " "))
(is= "this is a test" (replace-lexicon "this#is#a#test" "#" " "))
)
A hint is in the substring clojure.core$str in the error msg, which is the compiler's way of representing the function clojure.core/str.
Related
Basically, I have used slurp to get the contents of a file that is supposed to be a database. I've split the data already once and have a vector that contains all the information correctly. Now I would like to split each element in the vector again. This would give me a vector of vectors. My problem is I can't seem to find the right way to iterate through the vector and make my changes. The changes either don't work or are not stored in the vector.
Using doseq:
(doseq [x tempVector]
(clojure.string/split x #"|")
)
If I add a print statement in the loop it prints everything spaced out with no changes.
What am I doing wrong?
The str/split function returns a new vector of strings, which you need to save. Right now it is being generated and then discarded. You need something like this:
(ns xyz
(:require
[clojure.string :as str]))
(def x "hello there to you")
(def y (str/split x #" ")) ; save result in `y`
(def z (str/split x #"e")) ; save result in `z`
y => ["hello" "there" "to" "you"]
z => ["h" "llo th" "r" " to you"]
You can read clojure basics online here: https://www.braveclojure.com .
I recommend buying the book as it has more stuff than the online version.
If you have several strings in a vector, you can use the map function to split each of them in turn:
(def my-strings
["hello is there anybody in there?"
"just nod if you can hear me"
"is there anyone at home?"])
(def my-strings-split
(mapv #(str/split % #" ") my-strings))
my-strings-split =>
[["hello" "is" "there" "anybody" "in" "there?"]
["just" "nod" "if" "you" "can" "hear" "me"]
["is" "there" "anyone" "at" "home?"]]
To restructure your slurped lines of text into a collection of vectors of words you could do something like:
(use '[clojure.string :as str :only [split]])
(defn file-as-words [filename re]
(let [lines (line-seq (clojure.java.io/reader filename))
line-words (vec (mapv #(str/split %1 re) lines))]
line-words))
Here we define a function which first uses line-seq to slurp the file in and break it into a collection of lines, then we map an anonymous function which invokes clojure.string/split on each line in the initial collection, breaking each line up into a collection of words delimited by the passed-in regular expression. The collection of vectors-of-words is returned.
For example, let's say we have a file named /usr/data/test.dat which contains
Alice,Eating,001
Kitty,Football,006
May,Football,004
If we invoke file-as-words by using
(file-as-words "/usr/data/test.dat" #",")
you get back
[["Alice" "Eating" "001"] ["Kitty" "Football" "006"] ["May" "Football" "004"]]
Question from a total newbie with Clojure. Task is pretty simple, but I'm having hard time finding the best way to do this - I need to set up input, where user could give me a list (user should determine how long) of natural numbers and the program should just return a sum of these numbers.
Maybe this is totally wrong already:
(defn inputlist[naturallist]
(println "Enter list of natural numbers:")
(let[naturallist(read-line)] ))
Here is one way of doing it:
> lein new app demo
> cd demo
Edit the project.clj and src/demo/core.clj so they look as follows:
> cat project.clj
(defproject demo "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/tools.reader "1.1.3.1"] ]
:main ^:skip-aot demo.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
> cat src/demo/core.clj
(ns demo.core
(:require
[clojure.tools.reader.edn :as edn]
[clojure.string :as str] ))
(defn -main []
(println "enter numbers")
(let [input-line (read-line)
num-strs (str/split input-line #"\s+")
nums (mapv edn/read-string num-strs)
result (apply + nums) ]
(println "result=" result)))
with result
> lein run
enter numbers
1 2 3 <= you type this, then <enter>
input-line => "1 2 3"
num-strs => ["1" "2" "3"]
nums => [1 2 3]
result => 6
You may wish to start looking at some beginner books as well:
Brave Clojure
Living Clojure
Programming Clojure
The Clojure CheatSheet
The quickest way to do this I can think of is
#(apply + (map #(Integer/parseInt %) (re-seq #"\d+" (read-line))))
This defines an anonymous function which:
(read-line) - reads a line of text, hopefully containing numbers separated by non-numeric characters. So you type in something like "123 456 789"
(re-seq #"\d+" ...) - uses the regular expression \d+ to search for strings of consecutive digits. Each string of consecutive digits is added to a sequence, which is subsequently returned. So, for example, if you type in 123, 456, 789 the re-seq function will return the sequence '("123" "456" "789").
(map #(Integer/parseInt %) ...) - invokes the anonymous function #(Integer/parseInt %) for each element in the list returned by the re-seq invocation, creating another list of the results. So if the input is '("123" "456" "789") the output will be '(123 456 789).
(apply + ...) - applies the + function to the list of numbers, summing them up, and returns the sum.
Et voila! The end result is, you type in a string of numbers, separated by non-numeric characters, you get back the sum of those numbers. If you want to be a little neater, promote code re-use, and in general make this a bit more useful you could break this up into separate functions:
(defn parse-string-list [s]
(re-seq #"\d+" s))
(defn convert-seq-of-int-strings [ss]
(map #(Integer/parseInt %) ss))
(defn sum-numbers-in-seq [ss]
(apply + ss))
Invoking this in a Lisp-y way would look something like
(sum-numbers-in-seq (convert-seq-of-int-strings (parse-string-list (read-line))))
or, in a more Clojure-y way
(-> (read-line)
(parse-string-list)
(convert-seq-of-int-strings)
(sum-numbers-in-seq))
Best of luck.
Welcome to Clojure—and StackOverflow!
Here's how to do it:
(defn input-numbers-and-sum []
(print "Enter list of natural numbers: ")
(flush)
(->> (clojure.string/split (read-line) #"\s+")
(map #(Integer/parseInt %))
(reduce +)))
Here's how it works:
Calling print rather than println avoids printing a newline character at the end of the line. This way, the user's input will appear on the same line as your prompt.
Since there was no newline, you have to call flush to force the output buffer containing the prompt to be printed.
split splits what the user typed into a sequence of strings, divided where a regular expression matches. You have to say clojure.string/split rather than just split because split is not in Clojure's core library. clojure.string/ specifies the library. #"\s+" is a regular expression that matches any number of consecutive whitespace characters. So, if your user types " 6 82 -15 ", split will return ["6" "82" "-15"].
map calls the standard Java library function Integer.parseInt on each of those strings. Integer/parseInt is Clojure's Java interop syntax for calling a static method of a Java class. The #(...) is terse syntax that defines an anonymous function; the % is the argument passed to that function. So, given the sequence of strings above, this call to map will return a sequence of integers: [6 82 -15].
reduce calls the + function repeatedly on each element of the sequence of integers, passing the sum so far as an argument along with the next integer. map and reduce actually take three arguments; the next paragraph tells how the third paragraph gets filled in.
->> is the "thread-last macro". It rewrites the code inside it, to pass the output of each expression but the last as the last argument of the following expression. The result is:(reduce + (map #(Integer/parseInt %) (clojure.string/split (read-line) #"\s+")))Most people find the version with ->> much easier to read.
That might seem like a lot to do something very simple, but it's actually bread and butter once you're used to Clojure. Clojure is designed to make things easy to combine; map, reduce, and ->> are especially useful tools for hooking other functions together.
I've included links to the documentation. Those are worth a look; many contain typical examples of use.
There are other ways to parse numbers, of course, some of which are shown in the answers to this question. What I've written above is an "idiomatic" way to do it. Learn that, and you'll know a lot of the everyday, must-know techniques for programming in Clojure.
Yep, readline is the correct way to do it.
But each element from readlines is essentially an instance of java.lang.Character , and since you want the sum, you'd prefer to convert them to integer before summing the elements of list.
(defn input-list
[]
(print "Enter list of natural numbers")
(let [nums (read-line)]
(reduce + (map #(Integer/parseInt %) (clojure.string/split nums #"\s+")))
This might not be the most idiomatic way to do it, but feel free to tweak it.
Also, please do clean up on your variable/function names.
Edit : (Integer/parseInt %) might cause an error if used directly since the input is an instance of characters, not string. So we can use clojure.string/split to convert user input to a sequence of strings, and use Integer/parseInt % for conversion.
In fact, a more readable version can be written using thread-first macros :
(defn input-list []
(print "Enter list of natural numbers: ")
(->> (clojure.string/split (read-line) #"\s+")
(map #(Integer/parseInt %))
(reduce +)))
This is a more clojurey way to do it.
If you don't care about negative scenarios (wrong input, syntax issues), the quickest solution would be to evaluate the user's input putting it into parens:
(defn sum-ints []
(let [input (read-line)
ints (read-string (str "(" input ")"))]
(reduce + 0 ints)))
Usage:
user=> (sum-ints)
1 2 3 4 5
15
The read-string function evaluates an text expression (1 2 3 4 5) in that case. Since numeral literals turn into numbers, the result will be just a list of numbers.
I have this:
(defn page1 []
(layout/render
"index.html"
({:articles (db/get-articles)})))
The function
db/get-articles
returns a list of objects which have the key body. I need to parse the body of the articles and replace, if exists, a substring "aaa12aaa" with "bbb13bbb", "aaa22aaa" with "bbb23bbb" and so on in the bodies. How can I do that so it also won't consume plenty of RAM? Is using regex effective?
UPDATE:
The pattern I need to replace is : "[something="X" something else/]". where X is a number and it's unknown. I need to change X.
There can be many such patterns to replace or none.
I would just use Java's String.replace or String.replaceAll or clojure.string functions: replace/replace-first.
I wouldn't waste time for premature optimisations and first measure if the simple solution works. I am not sure how big the article contents are but I guess it shouldn't be an issue.
If it turns out you really need to optimise then maybe you should switch to streaming the contents of your articles from your data storage and either implement replace manually or using a library like streamflyer to perform modifications on the fly before sending the article contents to the HTTP response stream.
Something like this should be plenty fast:
(mapv
(fn [{:keys [body] :as m}]
(assoc m :body
(reduce-kv
(fn [body re repl]
(string/replace body re repl))
body
{"aaa12aaa" "bbb13bbb",
"aaa22aaa" "bbb23bbb"})))
[{:body "xy aaa12aaa fo aaa22aaa"}])
If you can guarantee that the string only occurs once you can replace replace by replace-first.
Regex works great in clojure:
(ns clj.core
(:use tupelo.core)
(:require
[clojure.string :as str]
)
(spyx (str/replace "xyz-aaa12aaa-def" #"aaa12aaa" "bbb13bbb"))
;=> (str/replace "xyz-aaa12aaa-def" #"aaa12aaa" "bbb13bbb") => "xyz-bbb13bbb-def"
From http://www.braveclojure.com/functional-programming/, the following code will trim whitespace and replace "lol" with "LOL".
(require '[clojure.string :as s])
(defn clean
[text]
(s/replace (s/trim text) #"lol" "LOL"))
(clean "My boa constrictor is so sassy lol! ")
; => "My boa constrictor is so sassy LOL!"
Now, according to the website the code below which reduce over functions is equivalent to what we have above.
(defn clean
[text]
(reduce (fn [string string-fn] (string-fn string))
[s/trim #(s/replace % #"lol" "LOL")]))
Question: I don't understand how the text parameter get passed into the anonymous function within reduce function. How can I write a similar code that explicitly pass the parameter text into the anonymous function within the reduce function ?
The reduce function takes an optional argument, the initial value of the reduction. If it is not provided, the first item of the last arg is used instead (which of course would not work at all in this case, but does work when you have a sequence of inputs that are of the same effective type as the initial value).
user> (defn clean
[text]
(reduce (fn [string string-fn] (string-fn string))
text
[clojure.string/trim #(clojure.string/replace % #"lol" "LOL")]))
#'user/clean
user> (clean "My boa constrictor is so sassy lol! ")
"My boa constrictor is so sassy LOL!"
The following code seems to run fine. I see it print "inside main" but I don't see a dataset being printed. small-sample.csv contains several lines like below. What is the correct way to print a dataset?
I modified the code and now see that the program throws an exception. If I remove the print, I don't get an exception but I see no output. Not sure what I am doing wrong?
;;small-sample.csv file
Gomez,Addams,father
Morticia,Addams,mother
Pugsley,Addams,brother
...
(use 'incanter.core
'incanter.io)
(ns getting-data.core
(:require [incanter.core :as incanter]) (:require [incanter.io :as io])
)
(defn -main
"Command-line entry point."
[& raw-args]
(try
(println "inside main")
(print((io/read-dataset "data/small-sample.csv")))
(catch Exception e (prn "in catch1"))))
)
I think you should simply remove the parenthesis in io/read-dataset, like this:
(print (io/read-dataset "data/small-sample.csv"))
from the docs:
Returns a dataset read from a file or a URL.
The extra parenthesis tries to evaluate the data-set