gpt4 book ai didi

clojure - 如何在 clojure 中遍历 ArrayMap?

转载 作者:行者123 更新时间:2023-12-02 04:42:07 24 4
gpt4 key购买 nike

我对 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确定外部点是否在向量的向量内。

最佳答案

Maps 实际上实现了 clojure.lang.ISeq 接口(interface),这意味着您可以对它们使用所有更高级别的序列操作。单个元素是 [key value] 形式的对,因此,要找到与谓词 in-city? 匹配的第一个元素,您可以例如使用一些:

(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]

像这样使用谓词没有多大意义,因为您丢失了产生 bool 结果的实际值。下面的怎么样?

(filter pos? [-1 0 1]) ;; => [1]
(remove pos? [-1 0 1]) ;; => [-1 0]

这会选择或丢弃与您的谓词匹配的值。在这里,您应该能够看到与您的城市边界示例的连接:您想要在 map 中找到包含给定点 p 的所有城市。但是 map 不是序列,不是吗?事实上,他们是:

(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]

其实有一个函数结合了mapfilter。它被称为 keep 并返回其谓词产生的每个非 nil 值。因此,您可以检查每对的第一个元素,然后返回第二个:

(keep
(fn [pair]
(when (pos? (second pair))
(first pair)))
{:a 0 b 1})
;; => [:b]

每当你看到自己使用大量的firstsecond,中间可能有一些rest,你应该想到< em>解构。它可以帮助您以一种简单的方式访问部分值,我不会在这里详细介绍,但它可以非常直观地与序列一起使用:

(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)

所以,我希望这对你有用。

关于clojure - 如何在 clojure 中遍历 ArrayMap?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20601497/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com