我对clojure(昨天开始学习)和函数式编程完全不熟悉所以请原谅我的无知。我一直在尝试阅读很多clojure文档,但其中很多都完全超出了我的想法。
我正在尝试迭代这个设置的ArrayMap:
{city1 ([[0 0] [0 1] [1 1] [1 0]]), city2 ([[3 3] [3 4] [4 4] [4 3]]), city3 ([[10 10] [10 11] [11 11] [11 10]])}
(^希望语法正确,就像我的终端正在打印一样)
其中城市名称映射到矢量矢量,这些矢量定义构成该城市边界的点。我需要将所有这些点与外部点进行比较,以确定外部点是否在这些城市之一,如果是,那么它在哪个城市。
我正在使用详细here的光线投射算法来确定外部点是否在矢量矢量内。
答案 0 :(得分:5)
Maps实际上实现了clojure.lang.ISeq
接口,这意味着您可以对它们使用所有更高级别的序列操作。单个元素是[key value]
形式的对,因此,为了找到与谓词in-city?
匹配的第一个元素,您可以例如使用some
:
(some
(fn [[city-name city-points]] ;; the current entry of the map
(when (in-city? the-other-point city-points) ;; check the borders
city-name)) ;; return the name of a matching city
cities)
您也可以使用keep
查找与谓词匹配的所有元素,但我猜您的示例中的城市之间没有重叠。
更新:让我们退一步,因为使用序列很有趣。我不打算深入研究所有序列类型,只使用向量([1 2 3 ...]
)作为例子。
好的,首先,让我们访问我们的向量:
(first [1 2 3]) ;; => 1
(rest [1 2 3]) ;; => [2 3]
(last [1 2 3]) ;; => 3
(nth [1 2 3] 1) ;; => 2
函数式编程的优点在于,函数只是可以传递给其他函数的值。例如,您可能希望将一个函数(假设为“将数字加2”)应用于序列中的每个元素。这可以通过map
:
(map
(fn [x]
(+ x 2))
[1 2 3])
;; => [3 4 5]
如果您还没有看到它,则会有一个功能值的简写,其中%
是第一个参数,%2
是第二个参数,依此类推:
(map #(+ % 2) [1 2 3]) ;; => [3 4 5]
这是简洁实用的,你可能会在野外看到很多 。当然,如果您的函数有名称或存储在var中(例如使用defn
),您可以直接使用它:
(map pos? [-1 0 1]) ;; => [false false true]
使用这样的谓词没有多大意义,因为你丢失了产生布尔结果的实际值。以下怎么样?
(filter pos? [-1 0 1]) ;; => [1]
(remove pos? [-1 0 1]) ;; => [-1 0]
这会选择或放弃与谓词匹配的值。在这里,您应该能够看到与城市边界示例的连接:您希望在地图中找到包含给定点p
的所有城市。但地图不是序列,是吗?确实他们是:
(seq {:a 0 :b 1}) ;; => [[:a 0] [:b 1]]
哦,我的,可能性!
(map first {:a 0 :b 1}) ;; => [:a :b]
(filter #(pos? (second %)) {:a 0 :b 1}) ;; => [[:b 1]]
filter
检索所有匹配的城市(及其坐标)但由于您只对名称感兴趣 - 这些名称存储为每对的第一个元素 - 您必须从每个元素中提取它,类似于以下(更简单)示例:
(map first (filter #(pos? (second %)) {:a 0 :b 1}))
:: => [:b]
实际上有一个功能将map
和filter
结合起来。它被称为keep
并返回其谓词产生的每个非nil
值。因此,您可以检查每对中的第一个元素,然后返回第二个元素:
(keep
(fn [pair]
(when (pos? (second pair))
(first pair)))
{:a 0 b 1})
;; => [:b]
每当您发现自己使用了很多first
和second
时,可能会有几个rest
,您应该想到解构。它可以帮助您轻松访问部分值,我不会在这里详细介绍,但它可以非常直观地用于序列:
(keep
(fn [[a b]] ;; instead of the name 'pair' we give the value's shape!
(when (pos? b)
a))
{:a 0 :b 1})
;; => [:b]
如果您只对第一个结果感兴趣,当然可以直接访问它并编写类似(first (keep ...))
的内容。但是,由于这是一个非常常见的用例,因此Clojure会向您提供some
。这就像keep
但不会超越第一场比赛。让我们深入了解您的城市示例,其解决方案现在应该开始有意义了:
(some
(fn [[city-name city-points]]
(when (in-city? p city-points)
city-name))
all-cities)
所以,我希望这对你有用。