How to chain function calls in Clojure? - clojure

Imagine I have a string which I want to transform as follows:
Remove all spaces.
Remove all dots.
Make the string lower-case.
One way to do it is this:
(defn my-function
[s]
(let
[
clean-string1 (clojure.string/replace s " " "")
clean-string2 (clojure.string/replace clean-string1 "." "")
clean-string3 (clojure.string/lower-case clean-string2)
]
;; ...
)
)
How can I "chain" the functions clojure.string/replace and clojure.string/lower-case so that
the output of (clojure.string/replace s " " "") is fed to the input of
(clojure.string/replace clean-string1 "." "") and its output is fed to the input of
(clojure.string/lower-case clean-string2)
so that I don't need intermediate variables clean-string1 and clean-string2?

You just do it the same way you would in any language. You're asking for function composition, which in math or non-lisp languages looks like f(g(x)). In lisp of course that's (f (g x)), but the principle is the same.
(require '[clojure.string :as s])
(s/lower-case (s/replace (s/replace s " " "") "." ""))
is the most straightforward answer. But it's rather unpleasant to have this level of nesting where the function names are so far removed from their extra arguments, and so most people would instead write
(-> s
(s/replace " " "")
(s/replace "." "")
(s/lower-case))
which is the same thing but just using -> to shuffle the forms around a bit for clarity.

Related

How to expand HugSQL parameter into multiple like statements

Does anyone know how this can be accomplished?
(get-lists ["free" "food"]) ->
Select name
From Lists
Where name like '%free%' and name like '%food%'
I have tried:
-- :name get-lists :? :*
Select id, name
from Lists
where
--~ (clojure.string/join "" (interpose " AND " (map #(str "name LIKE '%" % "%'") :sKeyWords)))
But of course that does not work. Can someone point me in the right direction please?
To form a query you can also use clojure.pprint/cl-format from clojure's standard lib, which is quite powerful and concise:
user> (require '[clojure.pprint :refer [cl-format]])
user> (cl-format nil "WHERE~{ name LIKE '%~a%' ~^AND~}"
["me" "you" "somebody"])
;;=> "WHERE name LIKE '%me%' AND name LIKE '%you%' AND name LIKE '%somebody%' "
Figured out answer. Putting here for anyone else who might need it.
-- :name get-lists :? :*
Select id, name
from Lists
--~ (str "WHERE "
(clojure.string/join " AND " (map #(str "name LIKE '%" % "%'")
(:key-words params))))

Using split on each element of a vector

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"]]

Sum of numbers in a list from user input

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.

Clojure: How to pass parameters when reducing over functions

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!"

Binding a local var in deftemplate for enlive

I'm brand new to clojure and the web development stack. I'm trying to use enlive to set values in an HTML template:
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
[:#project-name] (en/content (str "Name: " ((get-project id) :name)))
[:#project-desc] (en/content (str "Desc: " ((get-project id) :desc))))
This works fine to set my two HTML elements, but it involves a repeated call to my function get-project. At the moment this just reads from a local map, but eventually it will involve some external storage access, so I'd prefer to just perform it once in this function.
I was thinking of using let:
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
(let [project (get-project id)]
[:#project-name] (en/content (str "Name: " (project :name)))
[:#project-desc] (en/content (str "Desc: " (project :desc)))))
But this only affects the description element and ignores the name forms.
What is the best way to bind a local var within deftemplate?
If I have understood what you are trying to achieve; you could also try using the transformation macro provided by enlive.
(defn main-page [{:keys [name desc] :as project}]
(en/transformation
[:#project-name] (en/content (str "Name: " name)
[:#project-desc] (en/content (str "Desc: " desc))))
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
(main-page (get-project id)))
The code is untested, but I hope it conveys a different way to do what you need
Enlive's deftemplate macro expects a series of tag/content pairs after the args vector (the args vector is [id] in your example). You can't just stick a let in there because the macro isn't expecting a let form, so when it does its splicing everything gets messed up and results in the behavior you described above.
One way you could fix this would be to write your own deftemplate macro that allows binding definitions using the identifiers in the args vector. Example:
(alt/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
[project (get-project id)]
[:#project-name] (en/content (str "Name: " (project :name)))
[:#project-desc] (en/content (str "Desc: " (project :desc))))
The deftemplate macro is a simple wrapper around template, which uses snippet* and this is probably where you'd need to insert your changes:
(defmacro snippet* [nodes args & forms]
`(let [nodes# (map annotate ~nodes)]
(fn ~args
; You could add let bindings here since args are in scope
(doall (flatmap (transformation ~#forms) nodes#)))))
The other option—which might be simpler since you don't have to muck around in the library code—would be to add a level of indirection to your get-project function to cache results. You might try the core.cache library.