How to have multiple if statements inside a function in Clojure? - clojure

I want to do this in Clojure:
int i=1;j=2;k=3;
str r;
cin>>r;
if(r=="A")
cout<<i; (i.e., print 1)
if(r=="J")
cout<<j; (i.e., print 2)
if(r=="K")
cout<<k; (i.e., print 3)
else
do something else
I am doing it like this in clojure:
(defn str-to-num [c]
(if ( = (str (first c )) "A")
1
(java.lang.Integer/valueOf (str (first c))))
(if ( = (str (first c )) "J")
2
(java.lang.Integer/valueOf (str (first c))))
(if ( = (str (first c )) "K")
3
(java.lang.Integer/valueOf (str (first c))))
)
But, I'm getting an error. Could someone tell what I'm doing wrong?

All of the if blocks are run, one after the other, regardless of what any of them return. If you want if / else / chaining you should use cond or case (though the two branches of a standard if work fine if there are only two options).
Your first two if blocks can't do anything meaningful except throw an error. And that is exactly what will happen for most inputs.
"A", "J", and "K" are not valid numbers, so trying to parse them will throw an error.
The only meaningful things this function can do is return the first letter of a string as a number if it is parsible as one.
user> (str-to-num "A")
NumberFormatException For input string: "A" java.lang.NumberFormatException.forInputString (NumberFormatException.java:65)
user> (str-to-num "J")
NumberFormatException For input string: "J" java.lang.NumberFormatException.forInputString (NumberFormatException.java:65)
user> (str-to-num "K")
NumberFormatException For input string: "K" java.lang.NumberFormatException.forInputString (NumberFormatException.java:65)
user> (str-to-num "100")
1
perhaps you wanted something like:
user> (defn str-to-num [c]
(case (first c)
\A 1
\J 2
\K 3
(int (first c))))
#'user/str-to-num
user> (str-to-num "A")
1
user> (str-to-num "J")
2
user> (str-to-num "K")
3
user> (str-to-num "L")
76
user> (str-to-num "☃")
9731
Alternately:
user> (defn str-to-num [c]
(case (first c)
\A 1
\J 2
\K 3
(Integer/parseInt (subs c 0 1))))
#'user/str-to-num
user> (str-to-num "9")
9

The problem is with the form of your if statement
You have
(if ( = (str (first c )) "A")
1
(java.lang.Integer/valueOf (str (first c))))
The form of if is
(if (cond)
trueResult
falseResult)
So your "working version" will return 1 if you input A. If you input any other string, it actually throws an error. But, if an error were not thrown, all three if statements would be executed, and the result of the last one is actually returned.
This is closer to your C++ code:
(defn str-to-num [c]
(if ( = (str (first c )) "A") (print 1))
(if ( = (str (first c )) "J") (print 2))
(if ( = (str (first c )) "K") (print 3)))

Related

Removing the o coefficients along with their power and variable of a polynomial term in clojure

I created a polynomial function that returns the string based representation of the terms of polynomial added together, however i was having difficulties in removing terms from the string that contain the 0 coefficient and its giving me a different output
Below is my function:
(defn format-poly [poly]
(clojure.string/join ""
(map-indexed (fn [index item] (cond
(= index 0) item
(= (get item 0) \-) item
:default (str "+" item)))
(reverse (for [[pow coeff] (map-indexed vector (rseq (:coefficients poly))) ; remove whatever is causing a negative coeff to be ignored
]
(cond
(= coeff 0) (remove zero? [pow coeff])
(= coeff 1) (format "%s^%d",(:variable poly) pow)
(= pow 0) (format "%s",coeff)
:else
(format "%d%s^%d" coeff (:variable poly) pow)))))))
sample input:
(format-poly {:variable "x"
:coefficients [1 0 0 2 3 4]})
Expected output:
"x^6-2x^5+3x^4-4x^3+5x^2-6x^1+7"
i would propose gathering all the polynom print parts altogether into a flat collection, and then print all of them to string. Could look like this:
(defn format-poly [v coeffs]
(let [fmt-parts (mapcat (fn [coeff pow]
(when-not (zero? coeff)
[(if (neg? coeff) "-" "+")
(let [coeff (Math/abs coeff)]
(if (== 1 coeff) "" coeff))
(cond (zero? pow) ""
(== 1 pow) v
:else (str v "^" pow))]))
coeffs
(range (dec (count coeffs)) -1 -1))]
(apply str (if (= "+" (first fmt-parts))
(rest fmt-parts)
fmt-parts))))
user> (format-poly "x" [-1 -2 3 0 4 5])
;; "-x^5-2x^4+3x^3+4x+5"
user> (format-poly "x" [1 -2 3 0 4 5])
;; "x^5-2x^4+3x^3+4x+5"
user> (format-poly "x" [1 2 3 0 4 5])
;; "x^5+2x^4+3x^3+4x+5"

Clojure: First Function and Tick Marks

(ns main.core)
(defn andexp [& resty]
(println "here is resty:" resty)
(first (resty))
)
I am very new to Clojure coming from a Java and C background
In the repl, the input has to be in this format:
(andexp '(John is a beginner so have mercy))
I need to include the tick mark ('). I want my program to print out "John."
There are two things happening here:
You are using & resty to destructure for a list of all the arguments; that is why you see the output of ((John ...)) (note the double (()).
Next you are calling resty, when you write (resty), which results in an error (remember, parens in clojure are always meaningful and not just for groupting things togehter etc. like in curly braces languages).
So this will do what you want:
repl=> (defn andexp [resty]
(println "here is resty:" resty)
(first resty))
#'repl/andexp
repl=> (andexp '(a b c))
here is resty: (a b c)
a
If you really want to destructure (like stated in the comments), you have to put your arguments into another pair of [] to destructure on the passed in list. E.g.
repl=> (defn andexp [[f & resty]]
(println "here is f and resty:" f resty)
(first resty))
#'repl/andexp
repl=> (andexp '(a b c))
here is f and resty: a (b c)
b
More infos about
destructuring
If you write it like so:
(defn andexp
[& resty]
(println "here is resty:" resty)
(first resty))
you will get a result:
(andexp '(John is a beginner so have mercy)) #=>
here is resty: ((John is a beginner so have mercy))
(John is a beginner so have mercy)
but you probably wanted something more like this:
(defn andexp
[& resty]
(println "here is resty:" resty)
(first resty))
(println :result (andexp 1 2 3 4) ) ; no wrapping parens
with result:
here is resty: (1 2 3 4)
:result 1
Another option:
(defn andexp
[& resty]
(println "here is resty:" resty)
(first (first resty))) ; or `ffirst`
(println :result (andexp [1 2 3 4]) )
with result:
here is resty: ([1 2 3 4])
:result 1
It may help to clarify the answer if you add more info about the use-case or goal.
For background, please see the documentation listed here:
https://github.com/io-tupelo/clj-template#documentation

Return an else value when using recur

I am new to Clojure, and doing my best to forget all my previous experience with more procedural languages (java, ruby, swift) and embrace Clojure for what it is. I am actually really enjoying the way it makes me think differently -- however, I have come up against a pattern that I just can't seem to figure out. The easiest way to illustrate, is with some code:
(defn char-to-int [c] (Integer/valueOf (str c)))
(defn digits-dont-decrease? [str]
(let [digits (map char-to-int (seq str)) i 0]
(when (< i 5)
(if (> (nth digits i) (nth digits (+ i 1)))
false
(recur (inc i))))))
(def result (digits-dont-decrease? "112233"))
(if (= true result)
(println "fit rules")
(println "doesn't fit rules"))
The input is a 6 digit number as a string, and I am simply attempting to make sure that each digit from left to right is >= the previous digit. I want to return false if it doesn't, and true if it does. The false situation works great -- however, given that recur needs to be the last thing in the function (as far as I can tell), how do I return true. As it is, when the condition is satisfied, I get an illegal argument exception:
Execution error (IllegalArgumentException) at clojure.exercise.two/digits-dont-decrease? (four:20).
Don't know how to create ISeq from: java.lang.Long
How should I be thinking about this? I assume my past training is getting in my mental way.
This is not answering your question, but also shows an alternative. While the (apply < ...) approach over the whole string is very elegant for small strings (it is eager), you can use every? for an short-circuiting approach. E.g.:
user=> (defn nr-seq [s] (map #(Integer/parseInt (str %)) s))
#'user/nr-seq
user=> (every? (partial apply <=) (partition 2 1 (nr-seq "123")))
true
You need nothing but
(apply <= "112233")
Reason: string is a sequence of character and comparison operator works on character.
(->> "0123456789" (mapcat #(repeat 1000 %)) (apply str) (def loooong))
(count loooong)
10000
(time (apply <= loooong))
"Elapsed time: 21.006625 msecs"
true
(->> "9123456789" (mapcat #(repeat 1000 %)) (apply str) (def bad-loooong))
(count bad-loooong)
10000
(time (apply <= bad-loooong))
"Elapsed time: 2.581750 msecs"
false
(above runs on my iPhone)
In this case, you don't really need loop/recur. Just use the built-in nature of <= like so:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(def true-samples
["123"
"112233"
"13"])
(def false-samples
["10"
"12324"])
(defn char->int
[char-or-str]
(let [str-val (str char-or-str)] ; coerce any chars to len-1 strings
(assert (= 1 (count str-val)))
(Integer/parseInt str-val)))
(dotest
(is= 5 (char->int "5"))
(is= 5 (char->int \5))
(is= [1 2 3] (mapv char->int "123"))
; this shows what we are going for
(is (<= 1 1 2 2 3 3))
(isnt (<= 1 1 2 1 3 3))
and now test the char sequences:
;-----------------------------------------------------------------------------
; using built-in `<=` function
(doseq [true-samp true-samples]
(let [digit-vals (mapv char->int true-samp)]
(is (apply <= digit-vals))))
(doseq [false-samp false-samples]
(let [digit-vals (mapv char->int false-samp)]
(isnt (apply <= digit-vals))))
if you want to write your own, you can like so:
(defn increasing-equal-seq?
"Returns true iff sequence is non-decreasing"
[coll]
(when (< (count coll) 2)
(throw (ex-info "coll must have at least 2 vals" {:coll coll})))
(loop [prev (first coll)
remaining (rest coll)]
(if (empty? remaining)
true
(let [curr (first remaining)
prev-next curr
remaining-next (rest remaining)]
(if (<= prev curr)
(recur prev-next remaining-next)
false)))))
;-----------------------------------------------------------------------------
; using home-grown loop/recur
(doseq [true-samp true-samples]
(let [digit-vals (mapv char->int true-samp)]
(is (increasing-equal-seq? digit-vals))))
(doseq [false-samp false-samples]
(let [digit-vals (mapv char->int false-samp)]
(isnt (increasing-equal-seq? digit-vals))))
)
with result
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 2 tests containing 15 assertions.
0 failures, 0 errors.
Passed all tests
Finished at 23:36:17.096 (run time: 0.028s)
You an use loop with recur.
Assuming you require following input v/s output -
"543221" => false
"54321" => false
"12345" => true
"123345" => true
Following function can help
;; Assuming char-to-int is defined by you before as per the question
(defn digits-dont-decrease?
[strng]
(let [digits (map char-to-int (seq strng))]
(loop [;;the bindings in loop act as initial state
decreases true
i (- (count digits) 2)]
(let [decreases (and decreases (>= (nth digits (+ i 1)) (nth digits i)))]
(if (or (< i 1) (not decreases))
decreases
(recur decreases (dec i)))))))
This should work for numeric string of any length.
Hope this helps. Please let me know if you were looking for something else :).
(defn non-decreasing? [str]
(every?
identity
(map
(fn [a b]
(<= (int a) (int b)))
(seq str)
(rest str))))
(defn non-decreasing-loop? [str]
(loop [a (seq str) b (rest str)]
(if-not (seq b)
true
(if (<= (int (first a)) (int (first b)))
(recur (rest a) (rest b))
false))))
(non-decreasing? "112334589")
(non-decreasing? "112324589")
(non-decreasing-loop? "112334589")
(non-decreasing-loop? "112324589")

How to end dotimes clojure

I am trying to make a guess the number game in clojure but I keep getting an error saying I can only recur from tail position
(def n (rand-int 100))
(prn n)
(println "You have 10 guesses :D")
(println "HINT: My number is between 1 and 100")
(dotimes [i 10]
(def guess (read-line))
(if (= guess str(n))
(recur (println "Correct!") (println "Incorrect"))))
(I am new to clojure)
dotimes is used to execute the body for sideeffects that exact amount given; there is no means to break - except throwing
loop (or functions) are recur targets. Next you would have to count down the attempts so you can stop, if the user did not guess it:
(loop [attempts 10]
; ...
(recur (dec attempts)))
There are also other problematic things:
Don't def inside other forms. Use let instead.
str(n) will throw, as it will try to call n (ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn)
recuring with println looks fishy, since println returns always nil
How do you end dotimes? You don't. Try using loop instead. There are a lot of issues with your code but that's a start.
though this is discouraged and counterclojurish to even think of short circuiting the execution this way, it is still totally possible with macros (purely for education and fun)
(defmacro return [& x]
`(list '~'return (do ~#x)))
(defmacro dotimes+ [[i n] & body]
`(loop [~i 0 res# nil]
(cond (and (list? res#) (= '~'return (first res#))) (second res#)
(< ~i ~n) (recur (inc ~i) (do ~#body))
:else res#)))
can be used like this:
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return :short-circuited)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
:short-circuited
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
nil
user> (dotimes+ [i 10]
(println i))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
;; 6
;; 7
;; 8
;; 9
nil
notice, that it still expects the return macro to be called in tail position (similar to recur in loop macro)
(dotimes+ [x 4]
(println "attempt" (inc x))
(let [answer (read-line)]
(println "answer is:" answer)
(if (= answer "yes")
(return "YEAH!!!")
(println "WRONG!"))))

deduplicating a sequence in clojure

I need to define a function which takes a sequence and some functions which act on elements inside the sequence. It returns a sequence from the old sequence where the elements with duplicate function values are removed.
(defn dedup [seq & functions] ...)
for example, if
(f1 1) = 'a'
(f1 2) = 'a'
(f1 3) = 'c'
(f1 4) = 'd'
(f2 1) = 'za'
(f2 2) = 'zb'
(f2 3) = 'zc'
(f2 4) = 'zb'
then
(dedup [1 2 3 4] f1 f2)
returns a sequence of (1 3)
how do I do it?
EDIT:
edited the test values so as not to create misunderstanding
EDIT:
Below is the (not so functional) implementation for the case of only 1 function
(defn dedup [seq f]
(loop [values #{} seq1 seq seq2 '()]
(let [s (first seq1)]
(if (nil? s)
(reverse seq2)
(let [v (f s)]
(if (contains? values v)
(recur values (rest seq1) seq2)
(recur (conj values v) (rest seq1) (conj seq2 s))))))))
your example seems to contradict the text - it is returning values where the two functions agree.
(defn dedup [seq & fns]
(for [s seq :when (apply = (map #(% s) fns))] s))
(dedup [1 2 3 4]
#(case % 1 "a" 2 "a" 3 "c" 4 "d")
#(case % 1 "a" 2 "b" 3 "c" 4 "b"))
(1 3)
maybe that's a little too compact? #(... % ...) is equivalent to (fn [x] (... x ...)) and the map in dup runs over the functions, applying them all to the same value in the sequence.
you could also test with
(dedup [1 2 3 4] {1 "a" 2 "a" 3 "c" 4 "d"} {1 "a" 2 "b" 3 "c" 4 "b"})
(1 3)
ps i think maybe the confusion is over english meaning. "duplicate" means that a value repeats. so "a" "a" is a duplicate of "a". i suspect what you meant is "multiple" - you want to remove entries where you get multiple (distinct) values.
pps you could also use filter:
(defn dedup [seq & fns]
(filter #(apply = (map (fn [f] (f %)) fns)) seq))
where i needed to write one anonymous function explicitly because you can't nest #(...).