2024-01-16 20:52:50 -05:00

193 lines
5.1 KiB
Clojure

(ns core
(:require [clj-http.client :as client]
[clojure.string :as string]))
(def cookie (str "session=" (slurp "session")))
(defn get-puzzle-input
"retrieves and caches aoc input for 2023 given a day"
[day]
(let [file (java.io.File. (str "inputs/day" day ".txt"))]
(or (when (.exists file)
(string/split-lines (slurp file)))
(let [data (-> (str "https://adventofcode.com/2023/day/" day "/input")
(client/get {:throw-entire-message? true
:headers {"Cookie" cookie}})
:body)]
(spit file data)
(string/split-lines data)))))
(defn re-seq-pos
"re-seq that produces a seq of {:start :end :group}"
[pattern string]
(let [m (re-matcher pattern string)]
((fn step []
(when (. m find)
(cons {:start (. m start) :end (. m end) :group (. m group)}
(lazy-seq (step))))))))
(defn split-spaces [s]
(string/split s #" "))
(defmacro let-dbg [arg-list & body]
(let [pairs (partition 2 arg-list)
definitions (->> pairs
(map (fn [[a b]]
`[~a (let [temp# ~b]
(println '~a "=" temp#)
temp#)]))
(apply concat))]
`(let [~@definitions]
~@body)))
(defmacro comp>
"generates a closure that composes inputs with clojure.core/-> semantics"
[& forms]
`(fn [v#] (-> v# ~@forms)))
(defmacro comp>>
"generates a closure that composes inputs with clojure.core/->> semantics"
[& forms]
`(fn [v#] (->> v# ~@forms)))
(defmacro w-fn
"wraps s-expr such as java static methods or macro calls
in a lambda so they can be passed as function objects"
[f] `(fn [v#] (~f v#)))
(defn mapvf
"returns a function that does mapv over f when called with a coll"
[f] #(mapv f %))
(defn reducef
"returns a function that reduces over f when called with a coll"
([f] #(reduce f %))
([f init] #(reduce f init %)))
(defmacro fn-m
"like fn but memoizes return values, including recursive calls"
[name arglist & body]
`(let [dp# (atom {})
f# (fn ~name ~arglist
(or (@dp# [~@arglist])
(let [res# (do ~@body)]
(swap! dp# assoc [~@arglist] res#)
res#)))]
f#))
(defmacro defn-m
"like defn but for a memoized fn, see ajet.core/fn-m"
[name arglist & body]
`(def ~name (fn-m ~name ~arglist ~@body)))
(defmacro log [& body]
(let [exprs# (map (fn [e#]
`(let [e-res# ~e#]
(println "expr:" '~e#)
(println "result: " e-res#)
e-res#)) body)]
`(do ~@exprs#
nil)))
(defn apply-each
"[& fs vs]
fs is a list of funcitons of length N
vs is a list of values of length N
this function returns a vector where each value is the result of applying
an f from fs to the corresponding v from vs"
[& args]
;; TODO: add spec or manual validation for args
(let [n (/ (count args) 2)]
(->> (zipmap (take n args)
(take-last n args))
(mapv #((first %) (second %))))))
(defn apply-each-v
"[& fs vs]
fs is a list of funcitons of length N
v is a vector of values of length N
this function returns a vector where each value is the result of applying
an f from fs to the corresponding value from v"
;; TODO: add spec or manual validation for args
[& args]
(->> (zipmap (drop-last args)
(last args))
(mapv #((first %) (second %)))))
(defmacro assert-all [& assertions]
`(do ~@(map (fn [assertion]
`(assert ~assertion))
assertions)))
(defn partial< [f & args1]
(fn [& args2] (apply f (concat args2 args1))))
(comment
(let-dbg [a 1
b (inc a)]
(+ a b))
(defn abc [a b]
(+ a b))
(assert (= 1
(inc 0)
(dec 2)
(+ (/ 1 2)
(/ 1 2))))
(assert
(= ((comp> (+ 1) (/ 2) (abc 1) (inc) ;; currying / partial fn applicaiton
inc ;; named fn
#(+ 1 %) (fn [v] (+ v 1))) ;; using lambdas
5)
8))
(assert (= ((comp> (- 5) (- 2))
10)
3))
(assert (= ((comp>> (- 5) (- 2))
10)
7))
(map (w-fn Long/parseLong) ["123" "456"])
input-cache
((mapvf #(* 2 %)) [1 2])
((reducef + 0) [1 2])
(let [v 20]
(assert (= ((comp> #(* % 3) #(+ % 2) #(* % 4))
v)
((comp #(* % 4) #(+ % 2) #(* % 3))
v))))
(assert-all
(=
(apply-each #(* % 2) #(+ % 5) 1 2)
(apply-each-v #(* % 2) #(+ % 5) [1 2])
[2 7])
(=
(apply-each #(* % 2) #(+ % 5) 5 10)
(apply-each-v #(* % 2) #(+ % 5) [5 10])
[10 15]))
(log (+ 5 5)
(- 2 1))
(defn-m fib [x]
(if (< x 2)
x
(+ (fib (- x 2))
(fib (dec x)))))
(map (w-fn log) [1 2])
;; 2000+ digit number generated in <16ms (leveraging polymorphism and big-int)
;; using a seemingly naive O(n!) implementation (leveraging defn-m, autocaching)
(time (fib 10000N))
;
)