6
\$\begingroup\$

Problem

Given a square matrix of size N×N, calculate the absolute difference between the sums of its diagonals.

Input Format

The first line contains a single integer, N. The next N lines denote the matrix's rows, with each line containing N space-separated integers describing the columns.

Output Format

Print the absolute difference between the two sums of the matrix's diagonals as a single integer.

Sample Input

3
11 2 4
4 5 6
10 8 -12

Sample Output

15

Code

(ns hackerrank.core
  [:require [clojure.string :as s]])

(defn get-diagonal-sums-reducer
  [n]
  (fn
    [sums [line-number line]]
    (let
      [primary (nth line line-number)
       secondary (nth line (- n (+ 1 line-number)))]
      (assoc
        sums
        :primary (+ primary (:primary sums))
        :secondary (+ secondary (:secondary sums))))))

(let
  [n (Integer/parseInt (read-line))
   matrix (for
            [_ (range n)]
            (mapv #(Integer/parseInt %) (s/split (read-line) #"\s+")))
   matrix-enumerated (map-indexed vector matrix)
   sums (reduce
          (get-diagonal-sums-reducer n)
          {:primary 0 :secondary 0}
          matrix-enumerated)
   difference (- (:primary sums) (:secondary sums))]
  (println (max difference (- difference))))

Couple questions in particular:

  • can I avoid the get-diagonal-sums-reducer somehow? I'm only using it so that it closes over n.

  • am I doing this as lazily as possible or is there any place I'm using vectors when seqs would have worked?

  • is there a much less verbose way to do this? Doesn't feel like this would be as complicated in Python.

\$\endgroup\$
4
\$\begingroup\$

Could you have avoided get-diagonal-sums-reducer?

You probably could have used partial if you wanted, though I'm not sure it ends up that much better:

(defn diagnoal-sums-reducer [n sums [line-number line]]
  ...)

(reduce (partial diagonal-sums-reducer n) ...)

Is there a much less verbose way to do this.

I think there is. I can see why you've tried to use reduce, but your reducer is doing way more than it should be. Essentially, there's 3 things it's doing:

  • Calculating the coordinates of the diagonals for the line.
  • Getting the values of those coordinates.
  • Summing the values together with the previous values.

There might be some situations where you'd need to do all of those things in a single reduce, but I don't think this is one of those cases. It's also complicated by having to do this for both the primary and the secondary cases.

It ends up much simpler if you split the whole process out into the individual steps:

(defn sum [x] (apply + x))

(let [n 3
      matrix [[11 2 4]
              [4 5 6]
              [10 8 -12]]

      ;; Build up a list of coordinates for the diagonals.
      primary-coords (for [i (range n)] [i i])
      secondary-coords (for [i (range n)] [(- n i 1) i])

      ;; Extract the values of those coordinates
      primaries (map #(get-in matrix %) primary-coords)
      secondaries (map #(get-in matrix %) secondary-coords)]

    ;; Sum them, take the absolute difference
    (Math/abs (- (sum primaries) (sum secondaries))))
\$\endgroup\$
0
\$\begingroup\$

Is there a much less verbose way to do this?

(defn diagonalDifference [a]
  (Math/abs (- (reduce + (map #(apply nth %) (map vector a (range 0 (count a)))))
               (reduce + (map #(apply nth %) (map vector a (range (dec (count a)) -1 -1)))))))

Explanation

The sum of the first diagonal is:

(reduce + (map #(apply nth %) (map vector a (range 0 (count a))))) 

Going through this from the inside to the outside:

  1. Create a sequence of x-coordinates for each diagonal range 0 (count a)
  2. We zip this sequence with the matrix. The result is ([[11 2 4] 0] [[4 5 6] 1] [[10 8 -12] 2]) for the hackerrank example matrix.
  3. Now we apply the nth function to each vector in this sequence using map obtaining the diagonal [11 5 -12]
  4. We sum it up using reduce

The other diagonal is calculated exactly the same way, only the range is different. The rest should be clear.

Note: I tried factoring out some redundancies with letfn, but for me this is more readable.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.