如何在Clojure中对字符串向量进行双重循环?

3

我有一个函数,它会检查 CSV 文件中的城市是否拥有超过 500000 的人口。我将所有找到的城市保存到向量中,但现在想要两次遍历该向量,以查找彼此靠近(小于 500 公里)的城市对(我已经有一个检查两个城市是否接近的函数)。以下是我目前的代码:

(defn closest-city-pairs []
  (with-open [rdr (reader)]
    (vec
      (for [line (drop 1 (line-seq rdr))
            :let [y (string/split line #",")]
            :when (= true (large(y 0)))]
        (let [newVec (conj [] (y 0))]
          (for[x newVec
               y newVec
               :when (= true (close x y))]
            (let [newNewVec (conj [] (x y))]
              (println newNewVec))))))))

它似乎不想打印,但我的逻辑和所有括号似乎都是有道理的,而且也是按顺序的?任何帮助都将是非常好的。


for并不是你从命令式语言中所知道的那样。它是一个“列表推导式”。你不必在那里“停止”新的let - 只需在末尾添加另一个:let,并继续在第一个for内部(但我怀疑这不是你想要的 - 你现在有三个嵌套循环)。将数据的读取移动到一个函数中,然后移动过滤操作,最后只在最后找到接近的数据。拥有小型、纯净、适当命名的函数可以使测试和实验变得更加容易。 - cfrick
2个回答

0

以下是一个单元测试形式的大纲,列出了您可以做的事情:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(def nums [1 2 3 11 12 13])

(defn close?
  [x y]
  (<= (Math/abs (- x y)) 5))

(dotest
  (let [close-pairs (vec
                      (for [x nums
                            y nums
                            :when (and (not= x y)
                                    (close? x y))]
                        [x y]))]
    (spyx close-pairs))
  )

带有结果:

close-pairs => [[1 2] [1 3] [2 1] [2 3] [3 1] [3 2] 
                [11 12] [11 13] [12 11] [12 13] [13 11] [13 12]]

因此我们得到了很多重复项。我们可以通过将输出发送到一个集合中,然后删除重复的集合来消除它们:

  (let [close-pairs (vec
                      (distinct
                        (for [x nums
                              y nums
                              :when (and (not= x y)
                                      (close? x y))]
                          #{x y})))]
    (spyx close-pairs))

带有结果:

close-pairs => [#{1 2} #{1 3} #{3 2} #{12 11} #{13 11} #{13 12}]

如果你在 for 表达式中使用索引来代替每个值,就可以避免使用集合技巧(以及工作重复)。请参阅文档有关indexed函数的信息

(indexed [:a :b :c]) => ([0 :a] [1 :b] [2 :c])

然后我们可以像这样使用:when子句:

(dotest
  (let [nums-indexed (indexed nums)
        close-pairs  (vec
                       (for [[ix x] nums-indexed
                             [iy y] nums-indexed
                             :when (and (< ix iy)
                                     (close? x y) )]
                         [x y]))]
    (spyx close-pairs)))

带有结果:

close-pairs => [[1 2] [1 3] [2 3] [11 12] [11 13] [12 13]]

通过巧妙地使用:while子句,我们可以进一步减少重复循环,但这留给读者作为练习。

附注:您需要在project.clj中添加[tupelo "0.9.169"]:dependencies,以便访问spyxindexed函数。


(defn test [] (def newNewVec []) (with-open [rdr (reader)] (vec (for [line (drop 1 (line-seq rdr)) :let [y (string/split line #",")] :when (= true (large(y 0)))] (let [newVec (conj [] (y 0)) close-pairs (vec (distinct (for [x newVec y newVec :when (and (not= x y) (close x y))] #{x y})))] (spyx close-pairs)))))) - Richard Desouza
但是我遇到了一个错误:Exception in thread "main" java.lang.ClassCastException: java.lang.Character cannot be cast to clojure.lang.Named, compiling:( - Richard Desouza
你需要使用新代码更新你的问题,因为在评论中它无法工作。 - Alan Thompson

0
(require '[clojure.math.combinatorics :as comb])
(require '[clojure.data.csv :as csv])

(with-open [r (io/reader "cities.csv")]
        (let [cities (->> (csv/read-csv r) (into [] (comp cat (filter large-city?))))]
            (filter close-to-each-other? (comb/combinations cities 2))))

当我尝试实现这个时,它显示“在类路径上找不到clojure/math/combinatorics__init.class或clojure/math/combinatorics.clj”。 - Richard Desouza
@RichardDesouza 如果你使用类似于Leiningentools.deps这样的工具,你可以将库添加到类路径中。例如,在Leiningen下,在project.clj文件的:dependencies关键字下添加[org.clojure/math.combinatorics "0.1.6"]。请参考这个简单的例子 - user2609980

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接