Spoiler warning, this is the first part of Code Day Advent 6.
I tried to solve this problem in Clojure and in Scala. The Scala program went fine and ended within seconds on my Macbook Air. However, the Clojure version takes much longer. What is the problem?
(ns adventofcode.day6
(:require [clojure.java.io :as io]
[clojure.string :as string]))
(set! *warn-on-reflection* true)
(def ^:const grid-size 1000)
(defn make-grid []
(let [grid (make-array Long/TYPE grid-size grid-size)]
(doseq [i (range grid-size)
j (range grid-size)]
(aset grid i j -1))
grid))
(defn parse-line [l]
(let [[command left-top right-bottom]
(-> l
(string/replace "turn off" "turn-off")
(string/replace "turn on" "turn-on")
(string/replace "through " "")
(string/split
parse-coordinates (fn [s]
(let [parts (string/split s
(map
[left top] (parse-coordinates left-top)
[right bottom] (parse-coordinates right-bottom)]
[command left top right bottom]))
(defn process-line [grid command left top right bottom]
(doseq [i (range left (inc right))
j (range top (inc bottom))]
(case command
"turn-off" (aset grid i j -1)
"turn-on" (aset grid i j 1)
"toggle" (aset grid i j (* -1 (aget grid i j)))
(throw (Exception. "unrecognized command")))))
(defn count-lights [grid]
(reduce + (for [i (range grid-size)
j (range grid-size)
:let [value (aget grid ^Long i ^Long j)]
:when (pos? value)]
value)))
(defn the-answer []
(with-open [rdr (-> "input-day6.txt"
io/resource
io/reader)]
(let [grid (make-grid)]
(doseq [l (line-seq rdr)]
(apply process-line grid
(parse-line l)))
(count-lights grid))))
EDIT: I rewrote the transition vector solution and it has pretty decent performance (11 seconds on my Macbook Air and 7 on my Macbook Pro)
(ns adventofcode.day6faster
(:require [clojure.java.io :as io]
[clojure.string :as string]))
(set! *warn-on-reflection* true)
(def ^:const grid-size 1000)
(defn make-grid []
(vec (repeat (* grid-size grid-size) -1)))
(defn parse-line [l]
(let [[command left-top right-bottom]
(-> l
(string/replace "turn off" "turn-off")
(string/replace "turn on" "turn-on")
(string/replace "through " "")
(string/split
parse-coordinates (fn [s]
(let [parts (string/split s
(map
[left top] (parse-coordinates left-top)
[right bottom] (parse-coordinates right-bottom)]
[command left top right bottom]))
(defn set-grid! [t-grid grid-size i j v]
(let [pos (+ i (* j (dec grid-size)))]
(assoc! t-grid pos v)))
(defn update-grid! [t-grid grid-size i j f]
(let [pos (+ i (* j (dec grid-size)))]
(assoc! t-grid pos
(f (get t-grid pos)))))
(defn process-line [t-grid command left top right bottom]
(doseq [i (range left (inc right))
j (range top (inc bottom))]
(case command
"turn-off" (set-grid! t-grid grid-size i j -1)
"turn-on" (set-grid! t-grid grid-size i j 1)
"toggle" (update-grid! t-grid grid-size i j (fn [i] (* -1 i)))
(throw (Exception. "unrecognized command")))))
(defn count-lights [grid]
(count (filter pos? grid)))
(defn the-answer []
(with-open [rdr (-> "input-day6.txt"
io/resource
io/reader)]
(let [t-grid (transient (make-grid))]
(doseq [l (line-seq rdr)]
(apply process-line t-grid
(parse-line l)))
(count-lights (persistent! t-grid)))))
EDIT 2: now with loops, types and a single modified array. 1.5 seconds on my Macbook Air!
(ns adventofcode.day6singlearray
(:require [clojure.java.io :as io]
[clojure.string :as string]))
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def ^:const grid-size 1000)
(defn make-grid []
(let [l (* grid-size grid-size)
a (make-array Long/TYPE l)]
(loop [i 0]
(if (< i l)
(do (aset ^longs a i -1)
(recur (inc i)))
a))))
(defn parse-line [l]
(let [[command left-top right-bottom]
(-> l
(string/replace "turn off" "turn-off")
(string/replace "turn on" "turn-on")
(string/replace "through " "")
(string/split
parse-coordinates (fn [s]
(let [parts (string/split s
(map
[left top] (parse-coordinates left-top)
[right bottom] (parse-coordinates right-bottom)]
[command left top right bottom]))
(defn set-grid! [grid ^long i ^long j v]
(let [pos (+ i (* j (dec grid-size)))]
(aset ^longs grid pos ^long v)))
(defn toggle-grid! [grid ^long i ^long j]
(let [pos (+ i (* j (dec grid-size)))]
(aset ^longs grid pos
^long (* -1 (aget ^longs grid pos)))))
(defn process-line [grid command left top right bottom]
(let [operation (case command
"turn-off"
"turn-on"
"toggle"
(throw (Exception. "unrecognized command")))]
(loop [^long i left
^long j top]
(if (<= i ^long right)
(if (<= j ^long bottom)
(do (operation i j)
(recur i (inc j)))
(recur (inc i) top))))))
(defn count-lights [grid]
(count (filter pos? grid)))
(defn the-answer []
(with-open [rdr (-> "input-day6.txt"
io/resource
io/reader)]
(let [grid (make-grid)]
(doseq [l (line-seq rdr)]
(apply process-line grid
(parse-line l)))
(count-lights grid))))
source
share