为什么我的代码会生成一个空列表列表?

Why does my code generate a list of empty lists?

我有以下数据结构:有些场景可以是序列的一部分。

例如,假设我们有场景 sc026:

{:sceneId "sc026"}

它是 seq07 的一部分:

(def seq07
  {
   :SeqId    "seq07"
   :Desc     "Sequence name"
   :Scenes   [sc026]
   :Comments []

   }
  )

给定一个场景列表,我想为每个场景创建一个列表,其中包含特定场景所属的序列 ID 列表。

例子

假设有一个包含两个场景 sc026sc027 的列表。 sc026seq07 的一部分,sc027 不是任何序列的一部分。

我想要达到的结果是这样的:[["seq07"], []].

我尝试实现的

我有一个函数 generate-scene-overview,除其他外,它需要创建该列表。它具有以下签名:

(defn- generate-scene-overview
  [scene-list time-info seqs]

scene-list 是场景集合((filter some? my-scene-list) 的结果,其中 my-scene-list 是包含 nil 元素的场景列表)。

seqs 是序列列表。

seqs 中的序列可以是结构化的和非结构化的。非结构化的在 :Scenes 字段中有一个非空列表。

因此,在generate-scene-overview中我首先从seqs中提取非结构化场景:

unstructured-seqs (filter
                    (fn [cur-seq]
                      (let
                        [scenes (get cur-seq :Scenes)]
                        (not (empty? scenes))
                        )
                      )
                    seqs)

接下来我需要将非结构化序列转换为场景序列元组的集合:

unstructured-seq-tuples (compose-unstructured-tuple-list unstructured-seqs)

compose-unstructured-tuple-list定义如下

(defn- compose-unstructured-tuple-list
  [unstructured-seqs]
  (into []
        (map
          (fn [cur-seq]
            (let
              [
               scenes (get cur-seq :Scenes)
               seqId (get cur-seq :SeqId)
               scene-seq-tuples (into []
                                      (map
                                        (fn [cur-scene]
                                          (let [scene-id (get cur-scene :sceneId)]

                                            {
                                             :SceneId scene-id
                                             :SeqId   seqId
                                             }

                                            )
                                          )
                                        scenes
                                        )
                                      )

               ]
              scene-seq-tuples
              )
            )
          )
        unstructured-seqs
        )
  )

接下来,我需要将结构化序列的元组与非结构化序列的元组组合起来:

seq-tuples (set/union unstructured-seq-tuples structured-seq-tuples)

最后,seq-tuples被转换成每个场景的序列ID列表:

scene-seqs (compose-scene-seqs scene-list seq-tuples)

compose-scene-seqs定义如下:

(defn compose-scene-seqs
  [scene-list seq-tuples]
  (into [] (map (fn [cur-scene]
                  (let
                    [scene-id (get cur-scene :sceneId)]
                    (findSeqIdsBySceneId scene-id seq-tuples)
                    )
                  )
                scene-list
                )
        )
  )

findSeqIdsBySceneId 看起来像这样:

(defn findSeqIdsBySceneId
  [scene-id seq-tuples]
  (let
    [
     scene-tuples (filter (fn [cur-tuple]
                            (let [cur-tuple-scene-id (get cur-tuple :SceneId)]
                              (= scene-id cur-tuple-scene-id))
                            )
                          seq-tuples
                          )
     seqs (map (fn [cur-tuple]
                 (get cur-tuple :SeqId)
                 )
               scene-tuples
               )
     ]
    seqs
    )
  )

我的问题

当我在调试器中 运行 上述代码时,scene-seqs 只包含空集合。

它应该只包含一个场景 sc026 的非空集合(其中包含字符串 seq07)。

我是如何诊断问题的

我试图在自动化测试中重现该问题。

第一次尝试 -- findSeqIdsBySceneId:

(deftest findSeqIdsBySceneId-test
  (is (= ["seq07"]
         (findSeqIdsBySceneId "sc026" [{

                                        :SceneId "sc026"
                                        :SeqId   "seq07"
                                        }])
         )
      )
  (is (= ["seq07", "seq06"]
         (findSeqIdsBySceneId "sc026" [{

                                        :SceneId "sc026"
                                        :SeqId   "seq07"
                                        }
                                       {

                                        :SceneId "sc026"
                                        :SeqId   "seq06"
                                        }
                                       ])
         )
      )

  )

那些测试 运行,所以我为 compose-scene-seqs 写了几个测试:

(deftest compose-scene-seqs-test
  (is (= [["seq07"]]
         (let
           [
            scene-list [{:sceneId "sc026"}]
            seq-tuples [
                        {

                         :SceneId "sc026"
                         :SeqId   "seq07"
                         }
                        ]
            ]
           (compose-scene-seqs scene-list seq-tuples)
           )

         ))

  )

(deftest compose-scene-seqs-test2
  (is (= [["seq07"] []]
         (let
           [
            scene-list [
                        {:sceneId "sc026"}
                        {:sceneId "sc027"}
                        ]
            seq-tuples [
                        {

                         :SceneId "sc026"
                         :SeqId   "seq07"
                         }
                        ]
            ]
           (compose-scene-seqs scene-list seq-tuples)
           )

         ))

  )

(deftest compose-scene-seqs-test3
  (is (= [[] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] ["seq07"] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []]
         (let
           [
            scene-list   my-scene-list
            seq-tuples [
                        {

                         :SceneId "sc026"
                         :SeqId   "seq07"
                         }
                        ]
            ]
           (compose-scene-seqs scene-list seq-tuples)
           )

         ))

  )

全部运行.

如果我替换

scene-list   my-scene-list

scene-list   (filter some? перечень-сцен2)

我收到以下断言错误,但即使这样也有一个非空集合:

问题

我还能做些什么来诊断和修复错误?

更新 1:

完整代码可在 this GitHub gist.

中找到

我在以下测试中重现了错误:

(deftest compose-scene-seqs-test4
  (is (= [[] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] ["seq07"] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []]
         (let
           [
            scene-list   (filter some? перечень-сцен2)
            unstructured-seqs [seq07]
            unstructured-seq-tuples (compose-unstructured-tuple-list unstructured-seqs)
            seq-tuples (set/union unstructured-seq-tuples [])
            ]
           (compose-scene-seqs scene-list seq-tuples)
           )
         )
      )
  )

我可能会先将其重构为更小一些

我认为您可以通过创建一个新的数据结构来通过 reduce 一次完成此操作,其中场景名称是一个键,seqid 信息列表在您使用 reduce 遍历旧数据时使用 conj 构建这些列表

在调试方面,intellji 有一个步骤调试器,您可以使用它来观察列表构建和一个表达式 window,您可以使用它来执行 运行 命令,就像在 repl 中一样,但在上下文中你的断点应该会给你足够的观察力来理解你的问题

这是针对所述任务的完整工作解决方案:

(defn- contains-scene? [seq scene-id]
  (some #(= scene-id (:sceneId %)) (:Scenes seq)))

(defn- seq-ids-containing-scene [seqs scene-id]
  (keep #(and (contains-scene? % scene-id) (:SeqId %)) seqs))

(defn seq-ids-containing-scenes [seqs scenes]
  (map #(seq-ids-containing-scene seqs (:sceneId %)) scenes))

测试用例:

(def sc026 {:sceneId "sc026"})

(def sc027 {:sceneId "sc027"})

(def seq07 {:SeqId    "seq07"
            :Desc     "Sequence name"
            :Scenes   [sc026]
            :Comments []})

(seq-ids-containing-scenes [seq07] [sc026 sc027]) ;; => (("seq07") ())

我无法理解所尝试解决方案的逻辑。它引入了一个似乎没有增加价值的概念“非结构化”(与 Clojure 的解构不同)。我尝试重新创建问题,但发现提供的代码不完整,因此我无法就失败原因提供任何帮助。

这是第二种替代解决方案,它通过一次序列集合构建映射 scene-mapscene-map有场景id键和序列id集合作为对应值:

(defn seq-ids-containing-scenes* [seqs scenes]
  (let [maps (for [seq seqs
                   scene (:Scenes seq)]
               {(:sceneId scene) [(:SeqId seq)]})
        scene-map (apply merge-with into maps)]
    (map #(get scene-map (:sceneId %) []) scenes)))

更新:

我在问题中提供的原始代码中发现了错误。在函数 compose-unstructured-tuple-list 中,将第一个 map 替换为 mapcat

这是一个非常有趣的问题,因为它使用数据 (seqid) 结合更深层次的数据 (secnes)。从概念上讲,这似乎建议使用拉链,但这些很难处理,只有在您处理数据时才“值得”。如果您有很多这样的数据要处理,那么 specter 可能值得一看。

Stephans 解决方案比这次尝试更短,但我同意@arcanine 的单程解决方案。场景和序列表明多次通过可能不是问题,但无论如何这是一次通过的努力:

(defn collect-seqs [seqs scenes]
  (let [scene->seqs (->> seqs
                         (mapcat (fn seq-sc-pairs [sq]
                                   (->> sq
                                        :Scenes
                                        (keep (comp (set scenes) :sceneId))
                                        (map (partial vector (:SeqId sq))))))
                         ;; group collection of ([seqid sceneid], ...)
                         (group-by second))]
    (for [sc scenes]
      (map first (get scene->seqs sc)))))

请注意,如果地图是可以接受的解决方案,这可能会更短一些。这也可能意味着减少功能会起作用