I implemented a naive solution for printing a Pascal's Triangle of N depth which I'll include below. My question is, in what ways could this be improved to make it more idiomatic? I feel like there are a number of things that seem overly verbose or awkward, for example, this if
block feels unnatural: (if (zero? (+ a b)) 1 (+ a b))
. Any feedback is appreciated, thank you!
(defn add-row [cnt acc]
(let [prev (last acc)]
(loop [n 0 row []]
(if (= n cnt)
row
(let [a (nth prev (- n 1) 0)
b (nth prev n 0)]
(recur (inc n) (conj row (if (zero? (+ a b)) 1 (+ a b)))))))))
(defn pascals-triangle [n]
(loop [cnt 1 acc []]
(if (> cnt n)
acc
(recur (inc cnt) (conj acc (add-row cnt acc))))))
(defn pascal []
(iterate (fn [row]
(map +' `(0 ~@row) `(~@row 0)))
[1]))
Or if you're going for maximum concision:
(defn pascal []
(->> [1] (iterate #(map +' `(0 ~@%) `(~@% 0)))))
To expand on this: the higher-order-function perspective is to look at your original definition and realize something like: "I'm actually just computing a function f
on an initial value, and then calling f
again, and then f
again...". That's a common pattern, and so there's a function defined to cover the boring details for you, letting you just specify f
and the initial value. And because it returns a lazy sequence, you don't have to specify n
now: you can defer that, and work with the full infinite sequence, with whatever terminating condition you want.
For example, perhaps I don't want the first n
rows, I just want to find the first row whose sum is a perfect square. Then I can just (first (filter (comp perfect-square? sum) (pascal)))
, without having to worry about how large an n
I'll need to choose up front (assuming the obvious definitions of perfect-square?
and sum
).
Thanks to fogus for an improvement: I need to use +'
rather than just +
so that this doesn't overflow when it gets past Long/MAX_VALUE
.