从clojure中的序列获取元素

从clojure中的序列获取元素,clojure,nested-lists,lazy-sequences,Clojure,Nested Lists,Lazy Sequences,我知道Clojure中的列表和向量在大多数情况下几乎可以互换使用。这是一个让我吃惊的简单案例 (nth [2 4] 0) ;=> 2 (nth '(2 4) 0) ;=> 2 (get [2 4] 0) ;=> 2 (get '(2 4) 0) ;=> nil -- wtf??? get的文档讲述了如何映射一个键,但它可以很好地处理向量或集合。nth的文档甚至提到了get,只讨论了它们在边缘情况下的差异 我面临这种奇怪行为的现实情况是,我加载了一个yaml文件。它产生了

我知道Clojure中的列表和向量在大多数情况下几乎可以互换使用。这是一个让我吃惊的简单案例

(nth [2 4] 0) ;=> 2
(nth '(2 4) 0) ;=> 2
(get [2 4] 0) ;=> 2
(get '(2 4) 0) ;=> nil -- wtf???
get
的文档讲述了如何映射一个键,但它可以很好地处理向量或集合。
nth
的文档甚至提到了
get
,只讨论了它们在边缘情况下的差异

我面临这种奇怪行为的现实情况是,我加载了一个yaml文件。它产生了地图和列表的嵌套结构。我想用
进入
访问一个元素

(def form (parse-yaml some-yaml-file))
(def best-friend (get-in form [:friends 0 :first-name]))
它不起作用,因为
get-in
在内部使用
get
。因此,我有一个理论和实践问题:

  • get
    的这种行为是否被认为是正确的和预期的?如果是,请解释原因
  • 如何访问这种映射和列表结构中的嵌套元素

get
的行为是正确的和预期的
get
用于“键控”数据结构,其中值映射到键。这包括将索引映射到值1的向量和设置2

列表不提供对元素的随机访问;它们应该被线性地穿过。由于支持的访问模式如此不同,列表和向量绝对不能互换使用,核心Clojure collections库也不支持这种使用。(
nth
是一个函数的奇怪例子,它既执行低性能的常数或对数时间查找,又执行线性遍历;Clojure land中的怪兽)

当然,“修改”(在持久数据结构的意义上:创建修改过的副本)还有进一步的区别,例如
conj
的工作方式以及向量的
assoc
的可用性(如脚注中所述;替换列表中的一个元素需要重新生成整个前缀,直到该点为止)

如果您想对数据使用类似向量的访问模式,应该将其放入向量中。列表可以转换为向量(线性时间)使用
vec
。如果您处理的序列化格式不确定是否应为某些数据返回列表或向量,并且您的解析器不接受告诉它应使用哪种格式的选项,那么您可能必须自己进行一些后处理(
clojure.walk
可能很有用,尤其是
prewalk
postwark
函数;这假设只涉及基本的clojure数据类型)


1事实上,向量更为真实:它们是关联的,因此您可以将它们与
assoc
一起使用(
(assoc[01 2]0:foo)
返回
[:foo 1 2]
;只支持小于等于
(计算向量)
的索引,因为
将索引关联到向量中已经存在并立即超过末尾的索引)


2在本讨论中,可以将集合视为将其成员映射到自身。在Clojure中,这实际上是正确的,因为用作函数的集合在应用于它时返回成员本身——对于非成员,返回
nil
——也就是说,这就是实现在引擎盖下的样子。

米夏马尔齐克优秀答案的代码示例补充:

(def form
  {:friends
  '({:id 1, :first-name "bob"}
    {:id 2, :first-name "sue"})
   :languages
  '({:id 1, :name "Clojure"})})

(-> form :friends (nth 0) :first-name)
;=> "bob"

(def form'
  (clojure.walk/prewalk #(if (list? %) (vec %) %) form))

(get-in form' [:friends 0 :first-name])
;=> "bob"