【问题标题】:How to get an element in a matrix in clojure如何在clojure中获取矩阵中的元素
【发布时间】:2021-11-03 05:13:13
【问题描述】:

我有一个向量向量[[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]],其中包含检测到的板的 x,y 位置。

如何检索板p3x 位置?

这似乎是一个简单的任务,但我对 python 更熟悉,所以我不确定如何在 clojure 中执行此操作。

【问题讨论】:

  • 请添加您尝试过的代码以及失败的原因(例如错误、堆栈跟踪、日志等),以便我们对其进行改进。

标签: clojure


【解决方案1】:

我会选择这样的:

(def data [[:plate "p1" 0 1] [:plate "p2" 0 2] [:plate "p3" 1 1]])

(some (fn [[_ v x y]] (when (= v "p3") [x y])) data)
;;=> [1 1]

(some (fn [[_ v x y]] (when (= v "p123") [x y])) data)
;;=> nil

【讨论】:

    【解决方案2】:
    (def p '[[plate,"p1",0,1],[plate,"p2",0,2],[plate,"p3",1,1]])
    ;; be aware, 'p1' you can use in Python, but because in Clojure `'` at beginning
    ;; of a symbol is parsed as `quote`, you can't use `''` instead of `""` in contrast to Python.
    
    ;; create a nested map out of the vec of vecs
    (defn vecs-to-map [vecs]
      (reduce (fn [m [_ id x y]] (assoc m id {:x x :y y}))
              {}
              vecs))
    (def m (vecs-to-map p))
    ;;=> {"p1" {:x 0, :y 1}, "p2" {:x 0, :y 2}, "p3" {:x 1, :y 1}}
    
    ;; you can access such a nested list via `get-in` and giving the nested map
    ;; and the keys it should apply on it.
    (get-in m ["p3" :x])
    ;;=> 1
    

    由于一个键是字符串,另一个是关键字的不规则性是 不太好,我会用它们来做所有的关键字:

    (defn vecs-to-map [vecs]
      (reduce (fn [m [_ id x y]] (assoc m (keyword id) {:x x :y y}))
              {}
              vecs))
    
    (def m (vecs-to-map p))
    
    ;; access it by:
    (get-in m [:p3 :x])
    ;;=> 1
    

    其他想法

    我们忽略了 vec plate 的第一个元素。 假设还存在另一个向量,例如

    (def b '[[box "b1" 0 1] [box "b2" 0 2] [box "b3" 1 1]])
    

    如果我们想要一个包含:plate:box 的嵌套映射 外层作为键,我们必须改变vecs-to-map函数。

    (defn vecs-to-map [vecs]
      (reduce (fn [m [k id x y]] (assoc m (keyword k)
                                        (assoc (get m (keyword k) {})
                                               (keyword id) {:x x :y y})))
              {}
              vecs))
    

    然后我们可以通过以下方式生成包含所有内容的地图:

    (def m (vecs-to-map (concat p b)))
    
    ;; or alternatively in two steps:
    ;; (def m (vecs-to-map p))
    ;; (def m (merge m (vecs-to-map b)))
    
    
    m
    ;; => {:plate {:p1 {:x 0, :y 1}, :p2 {:x 0, :y 2}, :p3 {:x 1, :y 1}}, :box {:b1 {:x 0, :y 1}, :b2 {:x 0, :y 2}, :b3 {:x 1, :y 1}}}
    

    我们通过以下方式访问内容:

    ;; access through:
    (get-in m [:plate :p3 :x])
    ;; => 1
    
    (get-in m [:box :b2 :y])
    ;; => 2
    

    【讨论】:

    • 我绝对同意尽快转换为地图以使用更友好的数据格式。但是不要仅仅因为它们看起来不错就将字符串转换为关键字。来自外部世界的可能具有任何值的数据通常最好表示为字符串。关键字最好用作源文字。
    • 我会说你对vecs-to-map 的实现太具体了。我的建议是添加一些通用实用程序,例如 index-by: (defn index-by [kfn data] (into {} (map (juxt kfn identity)) data)) 然后 (def indexed (index-by second data))(some-> indexed (get "p2") (subvec 2)) => [0 2]
    • @leetwinski 确实,它太具体了。但我认为这在很大程度上取决于该行的外观以及人们想要拥有什么作为键和值......也许(defn index-by [kfn vfn data] (info {} (map (juxt kfn vfn)) data))。在你的情况下(def indexed (index-by second #(subvec % 2) data)) - 然后:(get indexed "p2") => [0 2]。就我而言:(def indexed (index-by #(keyword (second %)) #({:x (third %) :y (fourth %)}) data))。然后(get-in indexed [:p2 :x]) => 0
    • 最后一个啊修正:(def indexed (index-by #(keyword (second %)) (fn [v] {:x (nth v 2) :y (nth v 3)}) data))
    • @leetwinski 但编程有时就是要非常具体 - 不是我们必须抽象到最高级别的所有内容。
    【解决方案3】:

    您并没有真正提供有关您要执行的操作的太多上下文,但感觉就像您想将元组向量过滤到在第二个位置具有符号 p3' 的元组,然后只返回第三个和这种匹配的第四个元素?

    如果是这样,以下将起作用:

    dev=> (def plate :plate)
    #'dev/plate
    dev=> (def data [[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]])
    #'dev/data
    dev=> (let [[[_ _ x y]] (filter (comp #{'p3'} second) data)]
     #_=>   [x y])
    [1 1]
    

    这感觉不是很地道,所以也许你可以解释更多的上下文?

    注意:'p3' 是一个名称为 p3' 的符号,所以我想知道您的意思是 "p3" 是一个字符串吗?

    向量格式的向量似乎不太有利于您想要执行的那种访问 - 可能将其更改为哈希映射,其键是车牌 ID(如果这就是 p1p2、和p3 是?)合作会更好吗?

    编辑:作为对@leetwinkski 关于没有匹配结果的注释的回应,这里有一个替代方案:

    你可以使用when-first:

    dev=> (when-first [[_ _ x y] (filter (comp #{'p123'} second) data)]
     #_=>   [x y])
    nil
    dev=> (when-first [[_ _ x y] (filter (comp #{'p3'} second) data)]
     #_=>   [x y])
    [1 1]
    

    【讨论】:

    • 糟糕,我的意思是“p3”。我正在从外部来源接收向量向量,因此我正在尝试使用我正在按照您所说的那样做的工作:将元组向量过滤为在第二个位置具有符号“p3”的向量并返回3/4 元素。
    • 如果集合中没有这样的值,这会表现得很奇怪:对于#{"p10"},它会返回[nil nil],这可能不是你想要的
    • 是的。您可以使用when-first——我已经更新了我的答案以表明这一点(因为代码块在 cmets 中的格式不正确!)。
    【解决方案4】:

    根据my favorite template project,我会这样做。另请注意,在 Clojure 字符串中,始终使用双引号,例如 "p1"。单引号完全不同!

    (ns tst.demo.core
      (:use tupelo.core tupelo.test))
    
    (defn vec-has-label
      "Returns true iff a vector has the indicated label"
      [vec lbl]
      (= lbl (nth vec 1)))
    
    (defn vec->x
      "Given a vector, return the x value"
      [vec]
      (nth vec 2))
    
    (defn find-label
      [matrix label]
      (let [tgt-vecs (filterv #(vec-has-label % label) matrix) ; find all matching vectors
            x-vals   (mapv vec->x tgt-vecs)] ; extract the x values
        x-vals))
    

    单元测试显示代码在运行

    (dotest
      (isnt (vec-has-label '[plate "p1" 0 1] "p0"))
      (is   (vec-has-label '[plate "p1" 0 1] "p1"))
    
      (is= 9 (vec->x '[plate "p1" 9 1]))
    
      (let [matrix (quote
                     [[plate "p1" 0 1]
                      [plate "p2" 0 2]
                      [plate "p3" 1 1]])]
        (is= (find-label matrix "p3")
          [1])
      ))
    

    单元测试显示了“引用”包含一个或多个符号的数据结构的两种方式。如果多余的 plate 符号不存在,则不需要这样做。

    另见list of documentation sources

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多