How to break a for loop in Clojure? - clojure

I have the following function:
(defn next-transformation
[arr]
(let [
arr-len (count arr)
i-range (range 0 arr-len)
j-range (range 0 arr-len)
indexes (for [i i-range
j j-range]
(let [
xi (nth arr i)
xj (nth arr j)
]
(if (> xi xj)
[i j]
nil
)
)
)
non-nil-indexes (filter
(fn [elem]
(not (= elem nil))
)
indexes
)
]
(if (not (empty? non-nil-indexes))
(first non-nil-indexes)
nil
)
)
)
It returns the first element of an array of tuples [i j] which describe elments of the array arr for which arr[i] > arr[j] is true.
The for loop in the fragment below runs through every pair of i and j:
indexes (for [i i-range
j j-range]
(let [
xi (nth arr i)
xj (nth arr j)
]
(if (> xi xj)
[i j] ;; I want the loop to stop here
nil
)
)
)
How can I modify this for loop so that it stops once it finds the first relevant tuple (i. e. the loop should stop at the place marked with ;; I want the loop to stop here comment)?
Here is the equivalent code in Java:
private Integer[] next-transformation(final Integer[] arr) {
for (int i=0; i < arr.length; i++) {
for (int j=0; j < arr.length; j++) {
if (arr[i] > arr[j]) {
return new Integer[] {i, j};
}
}
}
}
Update 1:
As recommended by #CharlesDuffy, I replaced for with loop/recur:
(defn next-transformation
[arr]
(loop [i 0
j 0]
(let [
arr-len (count arr)
]
(if (and (< i arr-len)
(< j arr-len))
(let [
xi (nth arr i)
xj (nth arr j)
j-plus-1 (+ j 1)
i-plus-1 (+ i 1)
new-i (if (< j-plus-1 arr-len)
i
(+ i 1))
new-j (if (< j-plus-1 arr-len)
(+ j 1)
0)
]
(if (> xi xj)
;; We found it
[i j]
;; We haven't found it, recur
(recur new-i new-j)
)
)
nil ; We are at the end of the loop
) ; if
)
) ; loop
) ; defn

In the for list comprehension, use :when to filter for the tuples of interest, and use first to return just the first one:
(defn next-transformation [arr]
(first (for [i (range (count arr))
j (range (count arr))
:when (> (nth arr i) (nth arr j))]
[i j])))

Related

Beginner's clojure attempt to hackkerrank problem timing out

I'm attempting the following problem on hackerrank:
https://www.hackerrank.com/challenges/counting-valleys
but unfortunately my following clojure code is timing out on many test cases, and I don't know what makes it so inefficient. Please be lenient. I only have in total 2 hours of total clojure experience.
(require '[clojure.string :as str])
; Complete the countingValleys function below.
(defn countingValleys [n s]
(do
(def running 0)
(defn counter [elem]
(do
(cond
(= elem "D") (def running (+ running 1))
(= elem "U")(def running (- running 1))
)
running
)
)
(def valley-num 0)
(defn valley-count [a b]
(do
(if (and (= a "U") (= b 0))
(def valley-num (+ valley-num 1)))
)
)
(def heights (for [elem s] (counter elem)))
(doseq [[i j] (map vector s heights)]
(valley-count i j))
valley-num
)
)
(def fptr (get (System/getenv) "OUTPUT_PATH"))
(def n (Integer/parseInt (clojure.string/trim (read-line))))
(def s (read-line))
(def result (countingValleys n (str/split s #"")))
(spit fptr (str result "\n") :append true)
Dead easy python implementation of the same logic that took 5 minutes and passes all test cases:
def countingValleys(n, s):
list = []
for i in range(len(s)):
d = 0
if s[i] == "D":
d = 1
elif s[i] == "U":
d = -1
if len(list) == 0:
list.append(d)
else:
list.append(list[-1] + d)
num = 0
for i in range(len(s)):
if s[i] == "U" and list[i] == 0:
num += 1
return num
So I figured it out. The inefficiency was in this line:
(doseq [[i j] (map vector s heights)]
(valley-count i j))
Which can be replaced with:
(doall (map valley-count s heights))
and then all tests pass.
The slowness of your code is the least of its problems. The tools you ought to be employing are
pure functions,
the sequence library,
and, for speed, the prospect of transducers.
I like your underlying algorithm: count the cases where an up movement takes you to sea level.
We can express it idiomatically thus:
(defn countingValleys [n s]
(let [counter {\D 1, \U -1}
heights (reductions + (map counter s))
s-heights (map vector s heights)
valley-num (count (filter #{[\U 0]} s-heights))]
valley-num))
... or, using the ->> threading macro ...
(defn countingValleys [_ s]
(->> s
(map {\D 1, \U -1})
(reductions +)
(map vector s)
(filter #{[\U 0]})
(count)))
These are clearer and faster than yours.
It seems that you and HackerRank are actually using ClojureScript. Your use of "U" as an element of a string won't work in Clojure proper: you have to use the character \U.
Those of us who are JavaScript(Node JS) developers, my solution here works perfectly
// Complete the countingValleys function below.
function countingValleys(n, s) {
const min = 2;
const max = 1000000;
let valleys = 0;
let isInValley;
s = (typeof s === 'string') ? s.split('') : s;
if (s.length >= min && s.length <= max &&
n === parseInt(n, 0) &&
n >= min &&
n <= max &&
n === s.length) {
s.map(steps => ((steps === "U") ? 1 : -1))
.reduce((prev, next) => {
if (prev < 0 && !isInValley) {
isInValley = true;
}
if ((prev + next) === 0 && isInValley) {
valleys++;
isInValley = false;
}
return prev + next;
});
}
return valleys;
}

Add dolist variables to a list in LISP

I have two dolists. The first dolist has a variable i and the inner dolist has a variable j.
I want to make a final list li look like ((00)(01)(10)(11))
(setq mm '(0 1))
(setq li '())
(dolist (i mm)
(dolist (j mm)
(progn
(setq ans (cons ans (list (nth i mm)
(nth j mm)))))))
gives me
((((NIL 0 0) 0 1) 1 0) 1 1)
CL-USER 68 > (let ((ans ()))
(dolist (i mm (reverse ans))
(dolist (j mm)
(setq ans (cons (list (nth i mm)
(nth j mm))
ans)))))
((0 0) (0 1) (1 0) (1 1))
Got it.
The keyword "List" does the trick.
(dolist (i mm) (dolist(j mm) (progn (setq ans (append ans (list(list(nth i mm )(nth j mm))))))))

Idiomatic way to use for, while still maintaining high performance

I have a map that is sorted by its keys which contains data like this:
(def h {50 Text1
70 Text2
372 Text1
391 Text2
759 Text1
778 Text2
})
The map is sorted by Keys. The key (the number) can be interpreted as the position where the corresponding value was found in a large block of text. In the above example, "Text1" was found at position 50 in the text.
Now, I want to find all Texts that were found within k positions of each other. I define a function a like this:
(defn nearest [m k]
(for [m1 (keys m) m2 (keys m)
:when (and (> m2 m1) (not= (m m1) (m m2)) (< (- m2 m1) k))]
[m1 (get m m1) m2 (get m m2)]))
(nearest h 50)
; [[50 "Text1" 70 "Text2"] [372 "Text1" 391 "Text2"] [759 "Text1" 778 "Text2"]]
This works, but is too slow when the map m has 100s of thousands of elements. Because the for loop actually looks at all pairs of elements in the map. Since the map is sorted, for each element in the map, it is not necessary to check further elements, once the next element is already beyond k characters. I was able to write a version using loop and recur. But it is kind of unreadable. Is there a more natural way to do this using for? I am assuming for (:while ) should do the trick, but was not able to find a way.
(defn nearest-quick [m k]
(let [m1 (keys m) m2 (keys m)]
(loop [inp m res [] i (first m1) m1 (rest m1) j (first m2) m2 (rest m2)]
(cond
(nil? i) res
(nil? j)(recur inp res (first m1) (rest m1) j m2)
(= i j) (recur inp res i m1 (first m2) (rest m2))
(< j i) (recur inp res i m1 (first m2) (rest m2))
(= (inp i) (inp j)) (recur inp res i m1 (first m2) (rest m2))
(< (- j i) k) (recur inp (conj res [i (inp i) j (inp j)]) i m1 (first m2) (rest m2))
(>= (- j i) k) (recur inp res (first m1) (rest m1) (first (rest m1)) (rest (rest m1)))))))
Note: with a map with 42K elements, the first version takes 90 mins and the second version takes 3 mins.
One could probably exploit subseq when the map is a sorted-map.
(defn nearest
[m n]
(for [[k v] m
[nk nv] (subseq m < k < (+ k n))
:when (not= v nv)]
[k v nk nv]))
Code not benchmarked.
Clojure's for also has a :while modifier, so you can stop the iteration with a condition.
From whatever I have understood from you example:
(def h (sorted-map 50 "Text1"
70 "Text2"
372 "Text1"
391 "Text2"
759 "Text1"
778 "Text2"))
(->> (map #(-> [%1 %2]) h (rest h))
(filter (fn [[[a b] [x y]]] (< (- x a) 50)))
(map flatten))

Ways to quickly update an element of a matrix in Incanter, Clojure?

Suppose I have a 3x3 matrix
(def myMatrix (matrix (range 9) 3))
; A 3x3 matrix
; -------------
; 0.00e+00 1.00e+00 2.00e+00
; 3.00e+00 4.00e+00 5.00e+00
; 6.00e+00 7.00e+00 8.00e+00
I can use $ to get an element, say 2nd row 1st column
($ 1 0 myMatrix) ; --> 3
Is there any API method to quickly update an element and then return a matrix? e.g.
(update-matrix-by-element 2 1 myMatrix 4)
; A 3x3 matrix
; -------------
; 0.00e+00 1.00e+00 2.00e+00
; 4.00e+00 4.00e+00 5.00e+00
; 6.00e+00 7.00e+00 8.00e+00
The closest API methods I can find are bind-rows and bind-columns, and my current version of function using these two methods is
;note i j starts from 1 rather than 0
(defn update-matrix-by-element [i j myMatrix value]
(if (or (> i (count (trans myMatrix))) (> j (count myMatrix)) (< i 1) (< j 1) (not (integer? i)) (not (integer? j)))
myMatrix
(let [n (count myMatrix)
m (count (trans myMatrix))
rangeFn #(if (== %1 %2) %1 (range %1 %2 ))
m1 (if (== (dec i) 0) []
($ (rangeFn 0 (dec i)) :all myMatrix))
m2 (if (== i m) []
($ (rangeFn i m) :all myMatrix))
matrixFn #(if (matrix? % ) % [ %])
newRow (if (== (dec j) 0)
(bind-columns [value] (matrixFn ($ (dec i) (rangeFn j n ) myMatrix)))
(if (== j n )
(bind-columns (matrixFn ($ (dec i) (rangeFn 0 (dec j) ) myMatrix)) [value] )
(bind-columns (matrixFn ($ (dec i) (rangeFn 0 (dec j) ) myMatrix)) [value] (matrixFn ($ (dec i) (rangeFn j n ) myMatrix))))
)
]
; (prn " m1 " m1 ) (prn " m2 " m2 ) (prn " newrow " newRow)
(bind-rows m1 newRow m2))))
If you mean "quickly" in the sense of performance, then you may probably to look at core.matrix which is designed to support fast mutable matrix operations.
Example using the vectorz-clj implementation for core.matrix:
(def M (matrix [[1 2] [3 4]]))
=> #<Matrix22 [[1.0,2.0][3.0,4.0]]>
(mset! M 0 0 10)
=> #<Matrix22 [[10.0,2.0][3.0,4.0]]>
(time (dotimes [i 1000000] (mset! M 0 0 i)))
"Elapsed time: 28.895842 msecs" ;; i.e. < 30ns per mset! operation
This will be much faster than anything which requires construction of a new mutable matrix, especially as the matrices get bigger / have higher dimensionality.
I'm working on making core.matrix integrate neatly with Incanter, so you should be able to use core.matrix matrices transparently within Incanter before too long.

How do you replace Java nested for loops in Clojure?

I have created a very simple nested loop example and am struggling to write the equivalent Clojure code. I've been trying to do it by list comprehensions but cannot get the same answer. Any help appreciated.
public class Toy {
public static void main(String[] args) {
int maxMod = 0;
for (int i=0;i<1000;i++) {
for (int j=i;j<1000;j++) {
if ((i * j) % 13 == 0 && i % 7 == 0) maxMod = i * j;
}
}
System.out.println(maxMod);
}
}
Here's a list comprehension solution:
(last
(for [i (range 1000)
j (range 1000)
:let [n (* i j)]
:when (and (= (mod n 13) 0)
(= (mod i 7) 0))]
n))
In general, you want to use some sort of sequence operation (like dnolen's answer). However, if you need to do something that is not expressible in some combination of sequence functions, using the loop macro works as well. For this precise problem, dnolen's answer is better than anything using loop, but for illustrative purposes, here is how you would write it with loop.
(loop [i 0
max-mod 0]
(if (>= i 1000)
(println max-mod)
(recur (inc i)
(loop [j 0
max-mod max-mod]
(if (>= j 1000)
max-mod
(recur (inc j)
(if (and (= (mod (* i j) 13) 0)
(= (mod 1 7) 0))
(* i j)
max-mod)))))))
This is pretty much an exact translation of your given code. That said, this is obviously ugly, which is why a solution using for (or other similar functions) is preferred whenever possible.
List comprehensions create lists from other lists, but you want just a single value as result. You can create the input values (i and j) with a list comprehension, and then use reduce to get a single value from the list:
(reduce (fn [max-mod [i j]]
(if (and (zero? (mod (* i j) 13))
(zero? (mod i 7)))
(* i j)
max-mod))
0
(for [i (range 1000) j (range 1000)]
[i j]))