为什么我的代码会生成一个空列表列表?
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 列表。
例子
假设有一个包含两个场景 sc026
和 sc027
的列表。 sc026
是 seq07
的一部分,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-map
。 scene-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)))))
请注意,如果地图是可以接受的解决方案,这可能会更短一些。这也可能意味着减少功能会起作用
我有以下数据结构:有些场景可以是序列的一部分。
例如,假设我们有场景 sc026
:
{:sceneId "sc026"}
它是 seq07
的一部分:
(def seq07
{
:SeqId "seq07"
:Desc "Sequence name"
:Scenes [sc026]
:Comments []
}
)
给定一个场景列表,我想为每个场景创建一个列表,其中包含特定场景所属的序列 ID 列表。
例子
假设有一个包含两个场景 sc026
和 sc027
的列表。 sc026
是 seq07
的一部分,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-map
。 scene-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)))))
请注意,如果地图是可以接受的解决方案,这可能会更短一些。这也可能意味着减少功能会起作用