I am newbie in Clojure. I have following Expressions:
(= (__ "Dave") "Hello, Dave!")
(= (__ "Jenn") "Hello, Jenn!")
(= (__ "Rhea") "Hello, Rhea!")
In place of __, in all 3 places must be inserted same expression so that the equality check in all 3 cases are true. At this point i have come up with str "Hello, ". As i understand this should produce "Hello, Dave" "Hello, Jenn" "Hello, Rhea" How do i put "!" mark at the and of each string? (I can only "write" expression in place of __)
Thank you
You want to drop a function into the place of __.
This function shall take a string s and shall return a string that is based on s so as to satisfy the three test cases.
A possible function is
(fn [s] (str "Hello, " s "!"))
which can written using Clojure syntactic sugar
#(str "Hello, " % "!"))
Thus
(= (#(str "Hello, " % "!") "Dave") "Hello, Dave!")
Bonus: Using testing framework
Clojure comes with a nice testing library, clojure.test (I don't know why it is called an API, which would mean there is a component on the other side of the callable functions; it's just a library)
We can use the testing library for good effect:
(require '[clojure.test :as t]) ; make library visible
(def myfun (fn [s] (str "Hello, " s "!"))) ; our function as symbol myfun
(t/deftest test-stringmanip
(t/is (= (myfun "Dave") "Hello, Dave!"))
(t/is (= (myfun "Jenn") "Hello, Jenn!"))
(t/is (= (myfun "Rhea") "Hello, Rhea!")))
(t/run-tests) ; do it!
Related
I've got a Clojure file which I'm importing other library functions into with :require and :refer in the ns declaration.
I now want to eval some code in that namespace and have it call those libraries.
But when I call it, I get an "Unable to resolve symbol" for the referred function I'm trying to use.
I'm guessing I have to pass it explicitly in to the eval somehow but can't find any examples.
Second question. I'd ideally like to not use Clojure's ordinary eval at all but to run Babashka SCI. Is there a way to pass libraries from my Clojure environment into this?
Update. Code example.
(ns clj-ts.card-server
[:require
...
[patterning.layouts :refer [framed clock-rotate etc]]
...
)
...
(defn one-pattern
"Evaluate one pattern"
[data]
(let [pattern
(try
(eval (read-string data))
(catch Exception e
(let [sw (new java.io.StringWriter)
pw (new java.io.PrintWriter sw) ]
(.printStackTrace e pw)
(str "Exception :: " (.getMessage e) (-> sw .toString) ))) )
]
...
)
Then when calling one-pattern with the following as the data argument
(let [a-round
(fn [n lc fc]
(clock-rotate
n (std/poly
0 0.5 0.4 n
{:stroke lc
:fill fc
:stroke-weight 3})))
]
(a-round 8 (p-color 140 220 180) (p-color 190 255 200 100) )
)
I get an error
Caused by: java.lang.RuntimeException: Unable to resolve symbol: clock-rotate in this context
I am able to eval a form that has a refer'ed function. As the following code demonstrates.
user> (join " " ["foo" "bar"])
Syntax error compiling at (*cider-repl Projects/example:localhost:59334(clj)*:43:7).
Unable to resolve symbol: join in this context
user> (require '[clojure.string :refer [join]])
nil
user> (join " " ["foo" "bar"])
"foo bar"
user> (eval (read-string "(join \" \" [\"foo\" \"bar\"])"))
"foo bar"
user>
EDIT:
eval performs the evaluation in the context of the "current" namespace that is bound to *ns*. There are three ways I can think of to address your question - with examples below. I've tried to match what I think your code structure is.
Here are the contents of the file foo.clj that defines the eval function evalit and refers a library function (in this case join from clojure.string).
(ns myorg.example.foo
(:require [clojure.string :refer [join]]))
(defn evalit [s]
(eval (read-string s)))
We first load this myorg.example.foo namespace:
user> (require 'myorg.example.foo)
nil
First alternative: use the fully qualified symbol for the join as follows:
user> (myorg.example.foo/evalit "(clojure.string/join \" \" [\"boo\" \"baz\"])")
"boo baz"
Second alternative: Temporarily bind *ns* to the namespace that contains the refer'ed join. Note that in this case we can just use "join" rather than the fully qualified "clojure.string/join". But we still have to use the fully qualified symbol for evalit, since we are referencing it from a different namespace.
user> (binding [*ns* (find-ns 'myorg.example.foo)]
(myorg.example.foo/evalit "(join \" \" [\"boo\" \"baz\"])"))
"boo baz"
Third alternative: Switch to the namespace with the refer'ed function.
user> (in-ns 'myorg.example.foo)
#namespace[myorg.example.foo]
myorg.example.foo> (evalit "(join \" \" [\"boo\" \"baz\"])")
"boo baz"
Here we can use the unqualified symbols since we are in the namespace with the definition and refer.
OK.
I ended up doing this with https://github.com/babashka/babashka SCI
In my file I basically took all the functions I had imported and put them into a new map like this
(def patterning-ns
{'clojure.core
{
'clock-rotate clock-rotate
'poly poly
...
}
}
)
Once the function names that got imported from elsewhere are in the map called patterning-ns (under the namespace name of clojure.core) then they are visible by default to any code eval-ed with
(sci/eval-string data {:namespaces patterning-ns })
Eg in
(defn one-pattern
"Evaluate one pattern"
[data]
(try
(let [
pattern
(sci/eval-string
data
{:namespaces patterning-ns })
svg (make-svg 400 400 pattern)
]
svg
)
)
(catch java.lang.Exception e
(let [sw (new java.io.StringWriter)
pw (new java.io.PrintWriter sw) ]
(.printStackTrace e pw)
(println e)
(str "Exception :: " (.getMessage e) (-> sw .toString) )) )))
I have a simple function hello in my source.
(defn hello [n] (str "Hello" n))
In the test case I am calling the hello function. But seems like it is returning nil. Test case:
(deftest test (testing "string"
;(is (= "Hello x" (ql/hello "x")))))
(is (= (ql/hello "x") "Hello x"))))
I am getting the following error.
expected: (= (ql/hello "x") "Hello x")
actual: (not (= nil "Hello x"))
Why is it returning nil? If I call the hello function from repl I get "Hello x" followed by nil, but I think it is due to repl right? When I call hello from another function, shouldn't it return the string? I am running the test case from repl directly and not using lein.
From your description it seems that your actual hello function was defined as:
(defn hello [n]
(println "Hello" n))
When you run (hello "x") it prints Hello x to the console and returns nil (that's the behaviour of println).
To get your test passing you need to redefine your function in the REPL so it matches your version with str instead of println.
boot.user=> (require '[clojure.test :refer :all])
nil
boot.user=> (defn hello [n]
#_=> (println "Hello" n))
#'boot.user/hello
boot.user=> (is (= "Hello x" (hello "x")))
Hello x
FAIL in () (boot.user5664236129068656247.clj:1)
expected: (= "Hello x" (hello "x"))
actual: (not (= "Hello x" nil))
false
boot.user=> (defn hello [n]
#_=> (str "Hello " n))
#'boot.user/hello
boot.user=> (is (= "Hello x" (hello "x")))
true
boot.user=>
For example, let's say I have a list
'("The" " " "Brown" " " "Cow")
I want to turn this into
"The Brown Cow"
Is there a command in clojure that does this?
I would rather use apply for that:
(apply str '("The" " " "Brown" " " "Cow"))
Since it calls the function just once, it is much more efficient for large collections:
(defn join-reduce [strings] (reduce str strings))
(defn join-apply [strings] (apply str strings))
user> (time (do (join-reduce (repeat 50000 "hello"))
nil))
"Elapsed time: 4708.637673 msecs"
nil
user> (time (do (join-apply (repeat 50000 "hello"))
nil))
"Elapsed time: 2.281443 msecs"
nil
As Chris mentioned you can just use clojure.string/join
another way without using a library (assuming you don't want any spaces.) is:
(reduce str '("The" " " "Brown" " " "Cow"))
will return
"The Brown Cow"
str takes a list of things and turns them into one string. You can even do this: (str "this is a message with numbers " 2 " inside")
Please, people, just say 'No' to the lists! So simple with vectors:
(ns clj.demo
(:require [clojure.string :as str] ))
(def xx [ "The" " " "Brown" " " "Cow" ] )
(prn (str/join xx))
;=> "The Brown Cow"
Quoted lists like:
'( "The" " " "Brown" " " "Cow" )
are much harder to type and read, and also more error-prone than a simple vector literal:
[ "The" " " "Brown" " " "Cow" ]
Also cut/paste is much easier, as you don't need to worry about adding the quote or forgetting to add it.
Note that (str/join ... can also accept an optional separator as the first argument. You can either use the 1-char string like the first example or a "literal" as in the 2nd example:
(ns clj.demo
(:require
[clojure.string :as str]
[tupelo.core :as t] ))
(let [words ["The" "Brown" "Cow"]]
(t/spyx (str/join " " words))
(t/spyx (str/join \space words)))
with results
(str/join " " words) => "The Brown Cow"
(str/join \space words) => "The Brown Cow"
Just teaching myself clojure and wondering around:
I am trying to create a function that takes an argument and adds it to a string. Being a newbie, i dont know if i am doing it right or wrong but it is not working. I want it to say "Hello, Ron !"
(fn
[x]
((str "hello, " %1 " !") x) "Ron")
This might sound basic
java.lang.RuntimeException: Unable to resolve symbol: % in this
context, compiling:(NO_SOURCE_PATH:0)
The %1 syntax is for use with anonymous function literals, like this:
#(str "hello, " %1)
In your case, the argument is named, so you can use it directly:
(fn [x] (str "hello, " x "!"))
You can also name the function itself:
(defn hello [name] (str "hello, " name "!"))
You can either use a:
named function
(defn hello [name] (str "hello, " name " !"))
(hello "Ron")
anonymous function
((fn [name] (str "hello, " name " !")) "Ron")
http://clojure.org/functional_programming
Input: "Michael" "Julia" "Joe" "Sam"
Output: Hi, Michael, Julia, Joe, and Sam. (pay attention to the commas and the word "and")
Input: nil
Output: Hi, world.
Here is my first attempt:
(defn say-hi [& name]
(print "Hi," name))
user> (say-hi "Michael")
Hi, (Michael)
nil
user> (say-hi "Michael" "Julia")
Hi, (Michael Julia)
nil
Question:
How to implement default: (no input, say "Hi World!")
How to get rid of the parents around names in output?
How to implement the commas separation and add the conjunction word "and"?
First off, Clojure supports multi-arity functions, so you could do something like this to achieve default behaviour:
(defn say-hi
([] (say-hi "World"))
([& names] ...))
Then, what you want is to take a seq and join all the strings it contains together, using ", " in between. The clojure.string namespaces contains lots of string manipulation functions, one of them being clojure.string/join:
(require '[clojure.string :as string])
(string/join ", " ["Michael", "Julia"])
;; => "Michael, Julia"
But the last element of the seq should be concatenated using " and " as a separator, so you'll end up with something like this:
(require '[clojure.string :as string])
(defn say-hi
([] (say-hi "World"))
([& names]
(if (next names)
(format "Hi, %s, and %s!"
(string/join ", " (butlast names))
(last names))
(format "Hi, %s!" (first names)))))
Note that you have to differentiate between the single- and multi-name cases and (next names) basically checks whether the seq contains more than one element. (You could achieve the same by adding another arity to the function.)
(say-hi)
;; => "Hi, World!"
(say-hi "Michael")
;; => "Hi, Michael!"
(say-hi "Michael" "Julia" "Joe" "Sam")
;; => "Hi, Michael, Julia, Joe, and Sam!"
You can use clojure.string/join:
(use '[clojure.string :only [join]])
(defn sentencify [& elems]
(->>
[(join ", " (butlast elems)) (last elems)]
(remove empty?)
(join " and ")))
(defn say-hi [& name]
(print "Hi," (if name
(sentencify name)
"World!")))
A concise solution:
(defn say-hi [& names]
(let [names (case (count names)
0 ["world"]
1 names
(concat (butlast names) (list (str "and " (last names)))))]
(->> names, (cons "Hi"), (interpose ", "), (apply str))))
(say-hi)
;"Hi, world"
(say-hi "Michael")
;"Hi, Michael"
(say-hi "Michael" "Julia" "Joe" "Sam")
;"Hi, Michael, Julia, Joe, and Sam"
For long lists of names, you would want to eschew count, last, and butlast, maybe by pouring names into a vector first.
To print (as the question does) rather than return the formatted string, append print to the final form:
(->> names, (cons "Hi"), (interpose ", "), (apply str), print)