sunspots
sunspots

Reputation: 1057

Circularly shifting nested vectors

Given a nested vector A

[[1 2 3] [4 5 6] [7 8 9]]

my goal is to circularly shift rows and columns.

If I first consider a single row shift I'd expect

[[7 8 9] [1 2 3] [4 5 6]]

where the 3rd row maps to the first in this case.

This is implemented by the code

(defn circles [x i j]
     (swap-rows x i j))

with inputs

(circles [[1 2 3] [4 5 6] [7 8 9]] 0 1)

However, I am unsure how to go further and shift columns. Ideally, I would like to add to the function circles and be able to either shift rows or columns. Although I'm not sure if it's easiest to just have two distinct functions for each shift choice.

Upvotes: 3

Views: 1067

Answers (3)

mikera
mikera

Reputation: 106401

There's a function for that called rotate in core.matrix (as is often the case for general purpose array/matrix operations)

The second parameter to rotate lets you choose the dimension to rotate around (0 for rows, 1 for columns)

(use 'clojure.core.matrix)

(def A [[1 2 3] [4 5 6] [7 8 9]])

(rotate A 0 1)
=> [[4 5 6] [7 8 9] [1 2 3]]

(rotate A 1 1)
=> [[2 3 1] [5 6 4] [8 9 7]]

Upvotes: 2

omiel
omiel

Reputation: 1593

You can also use cycle:

(defn circle-drop [i coll]
  (->> coll
       cycle
       (drop i)
       (take (count coll))
       vec))

(defn circles [coll i j]
  (let [n (count coll)
        i (- n i)
        j (- n j)]
    (->> coll
         (map #(circle-drop i %))
         (circle-drop j))))

(circles [[1 2 3] [4 5 6] [7 8 9]] 2 1)
;; => [[8 9 7] [2 3 1] [5 6 4]]

Upvotes: 2

Michał Marczyk
Michał Marczyk

Reputation: 84369

(defn circles [xs i j]
  (letfn [(shift [v n]
            (let [n (- (count v) n)]
              (vec (concat (subvec v n) (subvec v 0 n)))))]
    (let [ys (map #(shift % i) xs)
          j  (- (count xs) j)]
      (vec (concat (drop j ys) (take j ys))))))

Example:

(circles [[1 2 3] [4 5 6] [7 8 9]] 1 1)
;= [[9 7 8] [3 1 2] [6 4 5]]

Depending on how often you expect to perform this operation, the sizes of the input vectors and the shifts to be applied, using core.rrb-vector could make sense. clojure.core.rrb-vector/catvec is the relevant function (you could also use clojure.core.rrb-vector/subvec for slicing, but actually here it's fine to use the regular subvec from clojure.core, as catvec will perform its own conversion).

Upvotes: 4

Related Questions