The code that you already suggest is great, except for the complexity of nth
as pointed out by @amalloy (which can be addressed by turning the lazy sequence into a vector). The loop
is a fundamental building block for looping. However, when looping, sequences are often involved and there are many functions for constructing sequences and expressing algorithms on sequences. Knowing these functions can save you some time and to some extent make algorithms simpler or more concise.
Here is alternative way of expressing your algorithm. You can construct a lazy sequence of variance by starting from an initial variance and iterate division by 2.
Here is an example of a sequence of length 7 with an initial variance of 3:
(take 7 (iterate #(/ % 2) 3))
;; => (3 3/2 3/4 3/8 3/16 3/32 3/64)
We can now pass a sequence constructed in this way to reduce to express the loop.
(defn deforms2 [points depth variance]
(reduce (comp vec deform)
;; Initial value
(vec points)
;; Sequence of variances that we reduce over
(take depth (iterate #(/ % 2) variance))))
This code is slightly different from your initial code, in that no deformation takes place if depth
has the value 0:
(= pts (deforms2 pts 0 my-variance))
;; => true
The equivalent modification of the original code would mean to initialize the looping variable np
with points
instead of (deform points variance)
, making things more intuitive, in my opinion.
The reduce function does two things for you when expressing an iterative algorithm:
- It stops the looping when the input sequence is empty.
- It steps to the next element of the input sequence at every iteration.
In other words, you don't have to worry about those two concerns meaning less risk of bugs. It is similar in philosophy to foreach loops found in other languages.
Note also that I reduce using (comp vec deform)
instead of just deform
. This is a quick fix (not necessarily an optimal fix) to address the complexity of indexing operations, as pointed out by @amalloy.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…