简体   繁体   English

从clojure中的序列中获取元素

[英]Get element from sequence in clojure

I understand that lists and vectors in Clojure can be used almost interchangeably in most situations. 据我所知,在大多数情况下,Clojure中的列表和向量几乎可以互换使用。 Here is a simple case that surprised me 这是一个让我感到惊讶的简单案例

(nth [2 4] 0) ;=> 2
(nth '(2 4) 0) ;=> 2
(get [2 4] 0) ;=> 2
(get '(2 4) 0) ;=> nil -- wtf???

The documentation of get talks mapping a key, but it works with vectors or sets just fine. get会话的文档映射一个键,但它适用于矢量或集合。 The documentation of nth even mentions get , talking about their differences only in edge-cases. 的文件nth甚至提到get ,谈论他们只在边缘的情况下的差异。

The real-world situation where I faced this weird behavior was that I loaded a yaml file. 我遇到这种奇怪行为的真实情况是我加载了一个yaml文件。 It resulted in a nested structure of maps and lists. 它产生了一个嵌套的地图和列表结构。 I wanted to access an element with get-in . 我想用get-in访问一个元素。

(def form (parse-yaml some-yaml-file))
(def best-friend (get-in form [:friends 0 :first-name]))

It doesn't work, because get-in uses get internally. 它不起作用,因为get-in使用内部get So I have a theoretical and a practical question: 所以我有一个理论和实际的问题:

  • Is this behavior of get considered correct and expected? 这种行为是否get认为是正确和预期的? If so, please, explain why. 如果是这样,请解释原因。
  • How can I access a nested element in such a structure of maps and lists? 如何在这种地图和列表结构中访问嵌套元素?

The behaviour of get is correct and expected. get的行为是正确和预期的。 get works on "keyed" data structures, in which values are mapped to keys. get “键控”数据结构的工作,其中值映射到键。 This includes vectors, which map indices to values 1 , and sets 2 . 这包括将索引映射到值1的向量,并设置2

Lists do not provide random access to elements; 列表不提供对元素的随机访问; they're meant to be traversed linearly. 它们意味着线性遍历。 Since the supported access patterns are so different, lists and vectors are absolutely not meant to be used interchangeably and the core Clojure collections library makes no effort to support such usage. 由于支持的访问模式是如此不同,列表和向量绝对不能互换使用,核心Clojure集合库不会支持这种用法。 ( nth is an odd example of a function which does perform both low-performance constant-or-logarithmic-time lookups and linear traversals; a strange beast in Clojure land). nth是一个函数的奇怪例子,它执行低性能的常数或对数时间查找线性遍历; Clojure土地中的一个奇怪的野兽)。

There are of course further differences to do with "modification" (in the persistent data structure sense: creating modified copies), such as the way in which conj works and the availability of assoc for vectors (as already mentioned in a footnote; replacing an element in a list involves rebuilding the entire prefix up to that point). 当然还有一些与“修改”(在持久性数据结构意义上:创建修改后的副本)有所不同,例如, conj工作方式和向量的assoc的可用性(如脚注中已提到的;替换为列表中的元素涉及重建整个前缀直到该点)。

If you'd like to use vector-like access patterns with your data, you should put it in a vector. 如果您想对数据使用类似矢量的访问模式,则应将其放在向量中。 Lists can be converted to vectors (in linear time) with vec . 可以使用vec将列表转换为向量(线性时间)。 If you're dealing with a serialization format where it's ambiguous whether lists or vectors should be returned for some data and your parser doesn't accept an option to tell it which it should use, you might have to do some post-processing yourself ( clojure.walk might be useful, in particular the prewalk and postwalk functions; that's assuming only basic Clojure data types are involved). 如果你正在处理一个序列化格式,它是不明确是否应该为某些数据返回列表或向量而你的解析器不接受告诉它应该使用哪个选项,你可能需要自己进行一些后处理( clojure.walk可能很有用,特别是prewalkpostwalk函数;假设只涉及基本的Clojure数据类型)。


1 In fact, more is true of vectors: they are associative, so you can use them with assoc ( (assoc [0 1 2] 0 :foo) returns [:foo 1 2] ; only indices up to (count the-vector) are supported, for assoc ing to indices which already exist in the vector and immediately past the end). 1实际上,向量更多是正确的:它们是关联的,所以你可以使用它们与assoc(assoc [0 1 2] 0 :foo)返回[:foo 1 2] ;只有索引最多(count the-vector)的支持,为assoc荷兰国际集团,其已经在载体中,并立即过去的结束)存在索引。

2 For the purposes of this discussion, sets can be considered to map their members to themselves. 2出于本讨论的目的,可以考虑使用集合将其成员映射到自己。 This is actually true in Clojure in the sense that a set used as a function returns the member itself when applied to it -- and nil for non-members -- and also in the sense that that's what the implementation looks like under the hood. 这在Clojure中实际上是正确的,因为作为函数使用的集合在应用于它时会返回成员本身 - 对于非成员则返回nil - 并且在某种意义上,这就是实现在幕后的实现。

Code example supplement to Michał Marczyk's excellent answer: 代码示例补充MichałMarczyk的优秀答案:

(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"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM