data/collection 中的球拍序列与内置序列
Racket sequences in data/collection vs built-in sequences
我一直在玩 data/collection
中的一些界面,到目前为止我很喜欢它。具有不同 Racket 集合(如列表、流和序列)的通用接口非常方便——特别是考虑到这些类型的接口的多样性,否则(list-*
、vector-*
、string-*
、stream-*
, sequence-*
, ... !).
但是这些接口是否与 Racket 中的内置序列配合得很好?具体来说,我 运行 遇到了这个错误:
(require data/collection)
(take 10 (in-cycle '(1 2 3)))
=>
; take: contract violation
; expected: sequence?
; given: #<sequence>
; in: the 2nd argument of
; (-> natural? sequence? sequence?)
; contract from:
; <pkgs>/collections-lib/data/collection/sequence.rkt
; blaming: top-level
; (assuming the contract is correct)
; at: <pkgs>/collections-lib/data/collection/sequence.rkt:53.3
函数 in-cycle
returns 是内置的 "sequence," 而 data/collections
提供的多态 take
需要它自己的特殊序列接口。
在这种特殊情况下,我可以手动定义一个流来替换内置的 in-cycle
,例如:
(define (in-cycle coll [i 0])
(stream-cons (nth coll (modulo i (length coll)))
(in-cycle coll (add1 i))))
... 这行得通,但是有一个 awful lot of built-in sequences defined 所以我想知道是否有更好的方法,也许 standard/recommended 来处理这个问题。也就是说,我们是否可以根据 data/collection 中定义的序列来利用所有内置序列,就像后者包装其他现有序列(如列表和流)一样?
正如@Sorawee Porncharoenwase 提到的,您可以使用 data/collection
中的 cycle
而不是 built-in in-cycle
。
您还可以将 sequence->stream
应用于 in-cycle
的结果,
因为球拍的流既是 built-in 又是 data/collection
序列。例如,
(take 10 (sequence->stream (in-cycle '(1 2 3 4))))
有点棘手。此表达式 (in-cyle '(1 2 3))
的计算结果为 Racket 序列。
球拍序列不同于 "generic sequences"(请参阅 data/collection 的文档)。
当您从 data/collection
请求 take
时,您会得到需要通用集合的 take
,因此
#lang racket
(require data/collection)
(take 10 (in-cycle '(1 2 3)))
会给你一个错误。
文档说以下内置数据类型可用作集合:
- 列表
- 不可变哈希表
- 不可变向量
- 不可变哈希集
- 不可变字典
- 流
所以我们需要将序列(in-cycle '(1 2 3))
转换成上面的其中一种。
@capfredf 提到的明显选择是 sequence->stream
.
#lang racket
(require data/collection)
(take 10 (sequence->stream (in-cycle '(1 2 3))))
这按预期工作。
在进一步研究之后,我想我对 Racket 和 data/collection
中的序列有了更好的理解。我会尽力总结其他答案和评论中提出的所有要点,并包括我自己的学习。
球拍序列,即来自 built-in 的序列,旨在成为所有有序集合的 generic interface,就像您可以使用 dict-*
函数来使用任何字典类型,包括哈希。此外,还有许多方便的实用程序提供内置序列,以便在不同场景中轻松处理有序数据,例如从集合中获取的元素序列,或在某个输入端口接收到的输入序列,或序列从字典中提取的 key-value 对——最后一个本质上不是 "ordered" 集合,但可以通过使用 built-in 序列接口将其视为一个集合。
因此我们可以认为内置序列具有双重目的:
- 作为有序数据的统一接口,并且
- 通过在每种情况下提供自然的序列接口实现,方便在不同场景中使用序列。
现在,虽然内置序列在理论上旨在成为有序集合的统一接口,但在实践中,由于它们的冗长,例如,它们并不是特别适用于此目的。 sequence-take
和 sequence-length
而不仅仅是我们用于列表的 take
和 length
。
data/collection
序列解决了这个缺点,因为它们的名称简短且规范,例如 take
而不是 sequence-take
。此外,这些序列还为内置序列提供的许多序列实用程序提供 drop-in replacements,例如 cycle
和 naturals
而不是 in-cycle
和 in-naturals
,以及使用通用 in
函数来导出任何序列的惰性版本以用于迭代(如 (in (naturals))
)。由于不可变,这些 data/collection
版本通常更多 "well-behaved",内置序列不能保证。因此,在许多情况下,data/collection
序列可以被认为是内置序列的 替代品 ,这在很大程度上接管了内置序列的两个目的中的第一个。
也就是说,在你处理序列的地方,考虑使用data/collection
序列代替内置序列,而不是作为一种方式使用 内置序列。
然而,关于第 (2) 点,以下是当前可作为 data/collection 序列处理的类型:
- 列表
- 不可变哈希表
- 不可变向量
- 不可变哈希集
- 不可变字典
- 流
(source)
这已经足够了,但还有更多场景可以推导出 common-sense 序列。对于以上未涵盖的任何此类情况,内置序列实用程序仍然有用,例如 in-hash
和 in-port
在 data/collection
序列中没有类似物。通常,在许多情况下,我们可以轻松导出内置序列(请参阅实用程序 here),但不能导出 data/collection
序列。在这些特殊情况下,我们可以简单地将如此获得的内置序列通过 sequence->stream
转换为流,然后通过更简单的 data/collection
序列接口使用它,因为流可以被视为任何一种类型的序列。
我一直在玩 data/collection
中的一些界面,到目前为止我很喜欢它。具有不同 Racket 集合(如列表、流和序列)的通用接口非常方便——特别是考虑到这些类型的接口的多样性,否则(list-*
、vector-*
、string-*
、stream-*
, sequence-*
, ... !).
但是这些接口是否与 Racket 中的内置序列配合得很好?具体来说,我 运行 遇到了这个错误:
(require data/collection)
(take 10 (in-cycle '(1 2 3)))
=>
; take: contract violation
; expected: sequence?
; given: #<sequence>
; in: the 2nd argument of
; (-> natural? sequence? sequence?)
; contract from:
; <pkgs>/collections-lib/data/collection/sequence.rkt
; blaming: top-level
; (assuming the contract is correct)
; at: <pkgs>/collections-lib/data/collection/sequence.rkt:53.3
函数 in-cycle
returns 是内置的 "sequence," 而 data/collections
提供的多态 take
需要它自己的特殊序列接口。
在这种特殊情况下,我可以手动定义一个流来替换内置的 in-cycle
,例如:
(define (in-cycle coll [i 0])
(stream-cons (nth coll (modulo i (length coll)))
(in-cycle coll (add1 i))))
... 这行得通,但是有一个 awful lot of built-in sequences defined 所以我想知道是否有更好的方法,也许 standard/recommended 来处理这个问题。也就是说,我们是否可以根据 data/collection 中定义的序列来利用所有内置序列,就像后者包装其他现有序列(如列表和流)一样?
正如@Sorawee Porncharoenwase 提到的,您可以使用 data/collection
中的 cycle
而不是 built-in in-cycle
。
您还可以将 sequence->stream
应用于 in-cycle
的结果,
因为球拍的流既是 built-in 又是 data/collection
序列。例如,
(take 10 (sequence->stream (in-cycle '(1 2 3 4))))
有点棘手。此表达式 (in-cyle '(1 2 3))
的计算结果为 Racket 序列。
球拍序列不同于 "generic sequences"(请参阅 data/collection 的文档)。
当您从 data/collection
请求 take
时,您会得到需要通用集合的 take
,因此
#lang racket
(require data/collection)
(take 10 (in-cycle '(1 2 3)))
会给你一个错误。
文档说以下内置数据类型可用作集合:
- 列表
- 不可变哈希表
- 不可变向量
- 不可变哈希集
- 不可变字典
- 流
所以我们需要将序列(in-cycle '(1 2 3))
转换成上面的其中一种。
@capfredf 提到的明显选择是 sequence->stream
.
#lang racket
(require data/collection)
(take 10 (sequence->stream (in-cycle '(1 2 3))))
这按预期工作。
在进一步研究之后,我想我对 Racket 和 data/collection
中的序列有了更好的理解。我会尽力总结其他答案和评论中提出的所有要点,并包括我自己的学习。
球拍序列,即来自 built-in 的序列,旨在成为所有有序集合的 generic interface,就像您可以使用 dict-*
函数来使用任何字典类型,包括哈希。此外,还有许多方便的实用程序提供内置序列,以便在不同场景中轻松处理有序数据,例如从集合中获取的元素序列,或在某个输入端口接收到的输入序列,或序列从字典中提取的 key-value 对——最后一个本质上不是 "ordered" 集合,但可以通过使用 built-in 序列接口将其视为一个集合。
因此我们可以认为内置序列具有双重目的:
- 作为有序数据的统一接口,并且
- 通过在每种情况下提供自然的序列接口实现,方便在不同场景中使用序列。
现在,虽然内置序列在理论上旨在成为有序集合的统一接口,但在实践中,由于它们的冗长,例如,它们并不是特别适用于此目的。 sequence-take
和 sequence-length
而不仅仅是我们用于列表的 take
和 length
。
data/collection
序列解决了这个缺点,因为它们的名称简短且规范,例如 take
而不是 sequence-take
。此外,这些序列还为内置序列提供的许多序列实用程序提供 drop-in replacements,例如 cycle
和 naturals
而不是 in-cycle
和 in-naturals
,以及使用通用 in
函数来导出任何序列的惰性版本以用于迭代(如 (in (naturals))
)。由于不可变,这些 data/collection
版本通常更多 "well-behaved",内置序列不能保证。因此,在许多情况下,data/collection
序列可以被认为是内置序列的 替代品 ,这在很大程度上接管了内置序列的两个目的中的第一个。
也就是说,在你处理序列的地方,考虑使用data/collection
序列代替内置序列,而不是作为一种方式使用 内置序列。
然而,关于第 (2) 点,以下是当前可作为 data/collection 序列处理的类型:
- 列表
- 不可变哈希表
- 不可变向量
- 不可变哈希集
- 不可变字典
- 流
(source)
这已经足够了,但还有更多场景可以推导出 common-sense 序列。对于以上未涵盖的任何此类情况,内置序列实用程序仍然有用,例如 in-hash
和 in-port
在 data/collection
序列中没有类似物。通常,在许多情况下,我们可以轻松导出内置序列(请参阅实用程序 here),但不能导出 data/collection
序列。在这些特殊情况下,我们可以简单地将如此获得的内置序列通过 sequence->stream
转换为流,然后通过更简单的 data/collection
序列接口使用它,因为流可以被视为任何一种类型的序列。