I want to create a function that will format a number with exactly as many decimal places as I want. Here's my attempt with cl-format:
=> (defn f [decimal-places n] (clojure.pprint/cl-format nil (str "~" decimal-places ",0$") n))
#'core/f
=> (f 5 55)
"55.00000"
=> (f 4 55)
"55.0000"
=> (f 3 55)
"55.000"
=> (f 2 55)
"55.00"
=> (f 1 55)
"55.0"
=> (f 0 55)
"55."
Notice the last one, with zero decimal places. I'm doing essentially this:
=> (clojure.pprint/cl-format nil "~0,0$" 55)
"55."
It has decimal separator - a dot - in there. How do make it render simply "55" (without a dot), in a way that I could easily (like with str in my example) make it to work with decimal-places greater than 0?
Although cl-format supports branching, in this case I'd stick with a simple if, because the arguments of format actually are quite different in both cases:
(defn f [decimal-places n]
(if (zero? decimal-places)
(clojure.pprint/cl-format nil "~D" (Math/round n))
(clojure.pprint/cl-format nil "~v$" decimal-places n)))
I round n to the nearest integer instead of just truncating with (int n).
An alternative is to remove any dot character at the end of the formatted string:
(defn undot [string]
(if (clojure.string/ends-with? string ".")
(subs string 0 (- (count string) 1))
string))
(defn f [decimal-places n]
(undot (clojure.pprint/cl-format nil "~v$" decimal-places n)))
Related
I'm following this pseudo code to convert decimal to binary recursively.
findBinary(decimal)
if (decimal == 0)
binary = 0
else
binary = decimal % 2 + 10 * (findBinary(decimal / 2)
This is what I have tried:
(defn binary [n]
(loop [res 0]
(if (= n 0)
res
(recur (res (* (+ (mod n 2) 10) (binary (quot n 2)))) )
)
)
)
But I get this error :
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/binary (form-init9002795692676588773.clj:6)
Any ideas how to fix the code to complete the task?
I realize, that this is about the journey and not the result. But to
have it mentioned: Long/toString can give you a string from a number with a wide
variety of radixes.
(Long/toString 123 2)
; → "1111011"
Here's a slightly different approach which allows recur to be used:
(defn find-binary [d]
(loop [ decimal d
digits '() ]
(if (= decimal 0)
(Integer. (clojure.string/join (map str digits)))
(recur (quot decimal 2) (conj digits (mod decimal 2))))))
In the loop we build up a collection of binary digits, adding each new digit at the beginning of the list so that we end up with the digits in the desired order left-to-right in the list. When the terminating condition is reached we convert the collection-of-digits to a collection-of-strings, join the collection of strings together into single string, and convert the string back to an integer.
Your psuedo code can be expressed pretty directly in clojure:
(defn find-binary [decimal]
(if (= decimal 0)
0
(+ (mod decimal 2) (* 10 (find-binary (quot decimal 2))))))
Examples:
user=> (find-binary 1)
1
user=> (find-binary 2)
10
user=> (find-binary 25)
11001
user=> (find-binary 255)
11111111
The error in your version is here:
(recur (res (* (+ (mod n 2) 10) (binary (quot n 2))))
Specifically, the complaint is that you are trying to use res (which has the value 0) as a function.
To be honest, I'm not sure how to do this with loop-recur. When I try it complains about recur not being from the tail position. Perhaps another answer can enlighten us!
Using recur:
;; returns a binary number string of the given decimal number
(defn find-binary [decimal]
(loop [n decimal
res ""]
(if (= n 0)
res
(recur (quot n 2)
(str (mod n 2) res)))))
But without loop:
(defn find-binary [decimal & {:keys [acc] :or {acc ""}}]
(if (= decimal 0)
acc
(find-binary (quot decimal 2) :acc (str (mod decimal 2) acc))))
The original attempt was the following, but it can't go far in the size of decimal:
(defn find-binary [decimal]
(loop [n decimal ;; number to be binarized
res '() ;; collector list
pos 0] ;; position of binary number
(if (= n 0)
(reduce #'+ res) ;; sum up collector list
(recur (quot n 2)
(cons (* (int (Math/pow 10 pos))
(mod n 2))
res)
(+ 1 pos)))))
For large numbers use:
I have a little programming issue that I'm trying to resolve in Clojure.
Say, I have a list with Integer values (they also include zeros). These values have a sum, which I want to decrease by a certain value. To get to this lower sum, I want to decrease the values in the list by ratio.
Say, I have the following list: [0, 10, 30, 40, 20, 0]. The sum is 100, and I want to decrease the sum to 90. I want to decrease the values by ratio, so the new list will be [0, 9, 27, 36, 18, 0].
However, this gets problematic when the numbers turn into fractions. When you round numbers (either with round, floor or ceil), you can end up with a sum that's off by 1 or 2. I can't seem to find an elegant solution. Everything I get consists of going through all the values once, and then going back to repair the offset. Any ideas?
Edit
To clarify the behaviour I want to see, the way it rounds doesn't really matter to me, as long as the sum is correct and the ratios of the numbers are approximately the same. I don't care care whether the total error is the smallest or that most are rounded down.
Additional requirements are that numbers are only allowed to stay equal or get lower, numbers should be >= 0, and the resulting list of numbers should be integers.
We can specify the function's requirements with clojure.spec. If we want the function to support integers w/arbitrary precision, sequences that sum to zero, empty sequences, etc., we could write this function spec:
(s/def ::natural-integer (s/and integer? (comp not neg?)))
(s/fdef dec-sum-int
:args (s/and (s/cat :new-sum ::natural-integer
:nums (s/coll-of ::natural-integer))
#(<= (:new-sum %) (apply +' (:nums %))))
:ret (s/coll-of ::natural-integer)
:fn (fn [{:keys [args ret]}]
(and (= (count (:nums args)) (count ret))
;; each output <= corresponding input
(every? true? (map <= ret (:nums args)))
(or (empty? ret)
(= (:new-sum args) (apply + ret))))))
Then st/check the original answer below to see failing examples, or see example invocations with s/exercise-fn.
Here's a version that satisfies the spec for your updated requirements. Most of the complexity is to ensure each output <= input when adjusting for rounding error:
(defn dec-sum-int [new-sum nums]
(let [sum (apply +' nums)
ratio (if (zero? sum) 1 (/ new-sum sum))
nums' (map #(bigint (*' % ratio)) nums)
err (- new-sum (apply + nums'))]
(loop [nums nums
nums' nums'
out []
err err]
(cond
(zero? err)
(into out nums')
(seq nums')
(let [[num & more] nums
[num' & more'] nums']
(if (pos? num)
(let [num'' (min num (+ num' err))]
(recur more more'
(conj out num'')
(- err (- num'' num'))))
(recur more more' (conj out num') err)))
:else out))))
(st/summarize-results (st/check `dec-sum-int))
{:sym playground.so/dec-sum-int}
=> {:total 1, :check-passed 1}
Original Answer
Here's a function to multiply each number in a collection by a ratio to reach some desired sum:
(defn adjust-sum [new-sum nums]
(let [sum (apply + nums)]
(map #(* % (/ new-sum sum))
nums)))
(adjust-sum 90 [0 10 30 40 20 0])
=> (0N 9N 27N 36N 18N 0N)
(map int *1)
=> (0 9 27 36 18 0)
For your example the results naturally come out as big integers. This is the only given example, but this problem lends itself well to property-based, generative testing. We can define properties that should hold for all examples and use test.check to test the function against many random examples we may not have imagined:
(tc/quick-check 10000
(prop/for-all [new-sum gen/int
nums (->> (gen/vector gen/int)
;; current approach fails for inputs that sum to zero
(gen/such-that #(not (zero? (apply + %)))))]
(= new-sum (apply + (adjust-sum new-sum nums)))))
=> {:result true, :num-tests 10000, :seed 1552170880184}
See updates above for handling examples with rounding error, or prior edits for handling negative numbers.
I don't think there is way to solve it without going through the list a second time to fix the rounding. Here is one solution using Largest Remainder Method:
(defn adj-seq
[input ratio rounding]
(let [;;
;; function to apply ratio to a number
;;
mul-ratio (partial * ratio)
;;
;; function to apply ratio and rounding to a number
;;
mul-ratio-r (comp rounding mul-ratio)
;;
;; sort oirignal input with largest remainder first
;; then applies ratio and rounding to each number
;;
rounded-list (->> input
(sort-by #(- (mul-ratio-r %)
(mul-ratio %)))
(map mul-ratio-r))
;;
;; sum of original numbers
;;
sum-input (reduce + input)
;;
;; calculate the delta between the expected sum and sum of all rounded numbers
;;
delta (- (mul-ratio-r sum-input) (reduce + rounded-list))]
;;
;; distribute delta to the rounded numbers in largest remainder order
;;
(->> rounded-list
(reductions (fn [[remain _] e]
;; increment number by 1 if remaining delta is >1
(if (pos? remain)
[(dec remain) (inc e)]
;; otherwise returns the rounded number as is
[0 e]))
;; delta is the initial value to feed to the reducing function
[delta])
;;
;; ignore the first output from the reducing function - which is the original delta
;;
rest
;;
;; get the adjusted number: ratio + rounding + delta-adj
;;
(map last))))
And a sample run:
(def input [0 10 30 40 20 0])
(def ratio 0.83)
(def rounding int)
(reduce + input)
;; => 100
(* ratio *1)
;; => 83.0
(adj-seq input ratio rounding)
;; => (25 17 8 33 0 0)
(reduce + *1)
;; => 83
Is this what you need?
(defn scale-vector
"Given `s`, a sequence of numbers, and `t`, a target value for the sum of
the sequence, return a sequence like `s` but with each number scaled
appropriately."
[s t]
(let [ratio (/ (reduce + (filter number? s)) t)]
(map #(if (number? %) (/ % ratio) %) s)))
(scale-vector [10 20 :foo 30 45.3 0 27/3] 21)
=> (1.837270341207349 3.674540682414698 :foo 5.511811023622047 8.32283464566929 0.0 1.6535433070866141)
(reduce + (filter number? (scale-vector [10 20 :foo 30 45.3 0 27/3] 21)))
=> 21.0
What's going on here:
We're assuming that s is a sequence of numbers; but it isn't necessarily an error if some element is not a number. Filtering for numbers allows us to cope gracefully is some elements are non-numeric; I've chosen to preserve non-numeric elements, but you could equally drop them.
I've done nothing special to exclude rational numbers from the output, and I can't see why you'd need to; but if you wanted to do that you could use (map double [1 1/2 22/7]) => (1.0 0.5 3.142857142857143).
But idiomatically, in Clojure, a number is just a number. Any function that accepts numbers should accept numbers. Rational numbers - what you are referring to as 'fractions' - are just numbers like any other numbers. Don't worry about them.
Say we have a function get-ints with one positional argument, the number of ints the caller wants, and two named arguments :max and :min like:
; Ignore that the implementation of the function is incorrect.
(defn get-ints [nr & {:keys [max min] :or {max 10 min 0}}]
(take nr (repeatedly #(int (+ (* (rand) (- max min -1)) min)))))
(get-ints 5) ; => (8 4 10 5 5)
(get-ints 5 :max 100) ; => (78 43 32 66 6)
(get-ints 5 :min 5) ; => (10 5 9 9 9)
(get-ints 5 :min 5 :max 6) ; => (5 5 6 6 5)
How does one write a Plumatic Schema for the argument list of get-ints, a list of one, three or five items where the first one is always a number and the following items are always pairs of a keyword and an associated value.
With Clojure Spec I'd express this as:
(require '[clojure.spec :as spec])
(spec/cat :nr pos-int? :args (spec/keys* :opt-un [::min ::max]))
Along with the separate definitions of valid values held by ::min and ::max.
I think this is a case when it is easier to write the specific code you need rather than trying to force-fit a solution using Plumatic Schema or some other tool that is not designed for this use-case. Keep in mind that Plumatic Schema & other tools (like the built-in Clojure pre- & post-conditions) are just a shorthand way of throwing an Exception when some condition is violated. If none of these DSL's are suitable, you always have the general-purpose language to fall back on.
A similar situation to yours can be found in the Tupelo library for the rel= function. It is designed to perform a test for "relative equality" between two numbers. It works like so:
(is (rel= 123450000 123456789 :digits 4 )) ; .12345 * 10^9
(is (not (rel= 123450000 123456789 :digits 6 )))
(is (rel= 0.123450000 0.123456789 :digits 4 )) ; .12345 * 1
(is (not (rel= 0.123450000 0.123456789 :digits 6 )))
(is (rel= 1 1.001 :tol 0.01 )) ; :tol value is absolute error
(is (not (rel= 1 1.001 :tol 0.0001 )))
While nearly all other functions in the Tupelo library make heavy use of Plumatic Schema, this one does it "manually":
(defn rel=
"Returns true if 2 double-precision numbers are relatively equal, else false. Relative equality
is specified as either (1) the N most significant digits are equal, or (2) the absolute
difference is less than a tolerance value. Input values are coerced to double before comparison.
Example:
(rel= 123450000 123456789 :digits 4 ) ; true
(rel= 1 1.001 :tol 0.01) ; true
"
[val1 val2 & {:as opts}]
{:pre [(number? val1) (number? val2)]
:post [(contains? #{true false} %)]}
(let [{:keys [digits tol]} opts]
(when-not (or digits tol)
(throw (IllegalArgumentException.
(str "Must specify either :digits or :tol" \newline
"opts: " opts))))
(when tol
(when-not (number? tol)
(throw (IllegalArgumentException.
(str ":tol must be a number" \newline
"opts: " opts))))
(when-not (pos? tol)
(throw (IllegalArgumentException.
(str ":tol must be positive" \newline
"opts: " opts)))))
(when digits
(when-not (integer? digits)
(throw (IllegalArgumentException.
(str ":digits must be an integer" \newline
"opts: " opts))))
(when-not (pos? digits)
(throw (IllegalArgumentException.
(str ":digits must positive" \newline
"opts: " opts)))))
; At this point, there were no invalid args and at least one of
; either :tol and/or :digits was specified. So, return the answer.
(let [val1 (double val1)
val2 (double val2)
delta-abs (Math/abs (- val1 val2))
or-result (truthy?
(or (zero? delta-abs)
(and tol
(let [tol-result (< delta-abs tol)]
tol-result))
(and digits
(let [abs1 (Math/abs val1)
abs2 (Math/abs val2)
max-abs (Math/max abs1 abs2)
delta-rel-abs (/ delta-abs max-abs)
rel-tol (Math/pow 10 (- digits))
dig-result (< delta-rel-abs rel-tol)]
dig-result))))
]
or-result)))
Based on the answer I got from the Plumatic mailing list [0] [1] I sat down and wrote my own conformer outside of the schema language itself:
(defn key-val-seq?
([kv-seq]
(and (even? (count kv-seq))
(every? keyword? (take-nth 2 kv-seq))))
([kv-seq validation-map]
(and (key-val-seq? kv-seq)
(every? nil? (for [[k v] (partition 2 kv-seq)]
(if-let [schema (get validation-map k)]
(schema/check schema v)
:schema/invalid))))))
(def get-int-args
(schema/constrained
[schema/Any]
#(and (integer? (first %))
(key-val-seq? (rest %) {:max schema/Int :min schema/Int}))))
(schema/validate get-int-args '()) ; Exception: Value does not match schema...
(schema/validate get-int-args '(5)) ; => (5)
(schema/validate get-int-args [5 :max 10]) ; => [5 :max 10]
(schema/validate get-int-args [5 :max 10 :min 1]); => [5 :max 10 :min 1]
(schema/validate get-int-args [5 :max 10 :b 1]) ; Exception: Value does not match schema...
I need to replace an integer with a string in clojure but only for 20% of the outputted integers.
(defn factor5 [x]
(if (= (mod x 3) (mod x 5) 0) "BuzzFizz"
(if (= (mod x 5) 0) "buzz"
(if (= (mod x 3) 0) "fizz" x))))
here i have a fizzbuzz program which prints out "fizz" if the number is a multiple of 3 or "buzz" if it is a multiple of 5 and "BuzzFizz" is printed if is a multiple of both. if an integer is neither of the above multiplies the integer gets printed. What i need is to print "Boom" instead of the integer but only for 20% of the integers.
some pseudo code
if(x == int){
print out Boom instead of x only for 20% }
else{
print out x}
I have very limited exprience in clojure as my pseudocode is java based
Please see the Clojure Cheatsheet for a comprehensive listing of functions.
The one you want is rand, and a test like:
(if (< (rand) 0.2) ....)
if you want the decision made randomly you could use one of the rand runctions in an if statement like so:
user> (defn x20% [x]
(if (rand-nth [true false false false false])
"Boom"
x))
#'user/x20%
user> (x20% 5)
5
user> (x20% 5)
5
user> (x20% 5)
"Boom"
user> (x20% 5)
5
there are also rand and rand-int. which you use is somewhat a matter of style and the specifics of your function:
user> (> 2 (rand-int 10))
true
user> (> 2 (rand-int 10))
true
user> (> 2 (rand-int 10))
false
user> (> 0.2 (rand))
true
user> (> 0.2 (rand))
(defn factor-5 [x]
(if (and (number? x) (zero? (rem x 1)))
(if (zero? (rand-int 5))
"Boom"
x)))
This returns the required value rather than printing it.
It tests that its argument is numeric, and - if so - that it is a
whole number value, which could be byte, short, int, ... .
(rand-int 5) chooses randomly from 0, 1, ... 4.
We can represent the number 12 as 2r001100 in clojure.
Is there a built-in function to print 2r001100 when given the number 12?
java.lang.Integer/toString will print numbers with arbitrary radix:
(Integer/toString 0xf2 2) ==> "11110010"
(Integer/toString 0xf2 16) ==> "f2"
(Integer/toString 0xf2 27) ==> "8q"
see cl-format
user=> (require '[clojure.pprint :refer (cl-format)])
nil
user=> (cl-format nil "2r~6,'0',B" 12)
"2r001100"
These functions generate and print strings using java.util.Formatter.
format
printf
But they don't do binary, so the best I could come up with is:
(fn [i] (str "2r" (Integer/toBinaryString i)))
All of these answers are good, but either won't support two's-complement for negative numbers (cl-format), or won't print out the correct number of bits based on the width of the data itself (e.g., calling Integer/toBinaryString or Integer/toString on a byte will not do what you want, especially for negative numbers).
Here's a solution that will correctly print out the exact bits of the underlying data:
(defn print-bits [b]
(let [class-name (.getName (class b))
is-byte (= "java.lang.Byte" class-name)
num-bits (clojure.lang.Reflector/getStaticField class-name "SIZE")
format-string (str "~" num-bits "'0b")
bin-str-fn #(clojure.lang.Reflector/invokeStaticMethod
(if is-byte "java.lang.Integer" class-name)
"toBinaryString"
(to-array [%]))
bit-string (if is-byte
(str/join (take-last 8 (bin-str-fn (Byte/toUnsignedInt b))))
(bin-str-fn b))]
(println (str (str/join (repeat (- num-bits (count bit-string)) \0))
bit-string))))
Test of extremes here, using (bit-shift-left 1 63), or 1000000000000000000000000000000000000000000000000000000000000000.
The cl-format solution provided gives me an integer overflow.
Integer/toBinaryString gives me Value out of range for int: -9223372036854775808.
But Long/toBinaryString gives me the string that I expected.