如何在clojure中迭代ArrayMap?

如何在clojure中迭代ArrayMap?,clojure,Clojure,我对clojure(昨天开始学习)和函数式编程一无所知,请原谅我的无知。我一直试图阅读大量clojure文档,但其中大部分内容完全超出了我的理解范围 我正在尝试迭代此设置的数组映射: {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]])} (^希望语法正确,这就是我的终端正在打印的内容) 其中,城市名称映射到定义构成该城市

我对clojure(昨天开始学习)和函数式编程一无所知,请原谅我的无知。我一直试图阅读大量clojure文档,但其中大部分内容完全超出了我的理解范围

我正在尝试迭代此设置的数组映射:

{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]])} 
(^希望语法正确,这就是我的终端正在打印的内容)

其中,城市名称映射到定义构成该城市边界的点的向量向量向量。我需要将所有这些点与外部点进行比较,以确定外部点是否位于其中一个城市,如果是,则位于哪个城市


我正在使用光线投射算法来确定一个外部点是否在一个向量向量内

映射实际上实现了
clojure.lang.ISeq
接口,这意味着您可以对它们使用所有高级序列操作。单个元素是成对的
[键值]
,因此,要在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]
这是简洁和有用的,你可能会在野外看到很多。当然,如果您的函数有名称或存储在变量中(例如通过使用
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
filter
。它被称为
keep
并返回其谓词生成的每个非
nil
值。因此,您可以检查每对的第一个元素,然后返回第二个元素:

(keep
  (fn [pair]
    (when (pos? (second pair))
      (first pair)))
  {:a 0 b 1})
;; => [:b]
每次你看到自己在使用大量的
first
s和
second
s,可能中间有一些
rest
s,你应该考虑解构。它帮助您以一种简单的方式访问部分值,我在这里不详细介绍,但它可以非常直观地用于序列:

(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提供了一些
。它就像
keep
,但不会超出第一次匹配的范围。让我们深入了解一下您的城市示例,您的解决方案现在应该开始有意义了:

(some
  (fn [[city-name city-points]]
    (when (in-city? p city-points)
      city-name))
  all-cities)

因此,我希望这对您有用。

谢谢!这很好用。我仍然不完全理解序列,所以肯定忽略了它。。。fn[[city name city points]]只是告诉它数据的格式吗?我花时间对代码进行了更全面的解释。我希望有帮助。(是的,这就是所谓的分解,它描述了数据的形状,使其易于访问。)非常感谢您抽出时间!这肯定澄清了很多事情,我以后一定会用这篇文章作为参考。这也让我对函数式编程更感兴趣。:)
(some
  (fn [[city-name city-points]]
    (when (in-city? p city-points)
      city-name))
  all-cities)