我一直在尝试思考如何实现algorithm来计算相对于点的多边形的匝数。目前实现如下:(注意更新,因此代码有效)
(defn winding-num
"Return winding number of polygon
see Alciatore "
[poly point]
; translate poly such that point is at origin
(let [translated-poly (map #(vec-f - % point) poly)]
; w is wind-num
(loop [vertices translated-poly w 0]
(cond
(= (count vertices) 1)
w
:else
(let [x1 (first (first vertices))
x2 (first (second vertices))
y1 (second (first vertices))
y2 (second (second vertices))]
(cond
(and (< (* y1 y2) 0)
(> (+ x1 (/ (* y1 (- x2 x1))
(- y1 y2)))
0))
(if (< y1 0)
(recur (rest vertices) (inc w))
(recur (rest vertices) (dec w)))
(and (zero? y1)
(> x1 0))
(if (> y2 0)
(recur (rest vertices) (+ w 0.5))
(recur (rest vertices) (- w 0.5)))
(and (zero? y2)
(> x2 0))
(if (< y1 0)
(recur (rest vertices) (+ w 0.5))
(recur (rest vertices) (- w 0.5)))
:else
(recur (rest vertices) w)))))))
我的问题是
map
,for
,reduce
等我可以想到使用for
和索引的实现,但我也听说最好不要使用索引。
是否存在处理矢量算法的惯用方法,在每次迭代中需要访问连续值?
答案 0 :(得分:4)
通常,如果要访问序列的连续值,一次两个,则可以使用分区功能。分区允许您指定组大小和步长:
user> (partition 2 1 (range 10))
((0 1) (1 2) (2 3) (3 4) (4 5) (5 6) (6 7) (7 8) (8 9))
答案 1 :(得分:1)
这实际上取决于算法的形状。一般来说,更高级别的结构比显式递归更容易理解,但有时问题的形状会使它不那么清晰。
其他注意事项:
rest
返回序列,而不是列表。这应该不重要。
你应该利用解构。例如:
(let [x1 (first (first vertices))
x2 (first (second vertices))
y1 (second (first vertices))
y2 (second (second vertices))
这可以替换为:
(let [[x1 y1] [x2 y2]] vertices] ... )
然而,使用reduce
实现这不是一个非常困难的算法:
(defn inc-dec
"Convenience function for incrementing and decrementing"
([condition i] (if condition (inc i) (dec i)))
([condition i amount] (if condition (+ i amount) (- i amount))))
(defn winding-num
[poly point]
(let [translated-poly (map #(map - % point) poly)
winding-reducer
(fn winding-reducer [w [[x1 y1] [x2 y2]]]
(cond
(and (< (* y1 y2) 0)
; r
(> (+ x1 (/ (* y1 (- x2 x1))
(- y1 y2)))
0))
(inc-dec (< y1 0) w)
(and (zero? y1) (> x1 0))
(inc-dec (> y2 0) w 0.5)
(and (zero? y2) (> x2 0))
(inc-dec (< y1 0) w 0.5)
:else w))
]
(reduce winding-reducer 0 (partition 2 1 translated-poly))))
答案 2 :(得分:0)
以下代码使用(map func seq (rest seq))
来处理算法使用的点对。它还解决了原始实现的两个问题:
无论是否通过将第一个点重复为最后一个点来指定多边形,它都有效,即为两者提供相同的结果
[[1 1][-1 1][-1 -1][1 -1]] and
[[1 1][-1 1][-1 -1][1 -1][1 1]]
它也适用于在正x轴上具有连续点的多边形,而原始(和引用的伪代码)将沿着x减去每个线段的1/2
-轴。
(defn translate [vec point]
(map (fn [p] (map - p point)) vec))
(defn sign [x]
(cond (or (not (number? x)) (zero? x)) 0
(pos? x) 1
:else -1))
(defn winding-number [polygon point]
(let [polygon (translate (conj polygon (first polygon)) point)]
(reduce +
(map (fn [[x1 y1][x2 y2]]
(cond (and (neg? (* y1 y2))
(pos? (- x2 (* y2 (/ (- x2 x1) (- y2 y1))))))
(sign y2)
(and (zero? y1) (pos? x1))
(sign y2)
(and (zero? y2) (pos? x2))
(sign y1)
:else 0))
polygon (rest polygon)))))