为什么字符串不是 scheme/racket 中的字符列表?

Why is a string not a list of characters in scheme/racket?

我习惯的是,字符串只是一个列表或字符数组,就像在大多数类 C 语言中一样。但是,在我使用的方案实现中,包括 Chicken 和 Racket,字符串与字符列表不同。 (car "mystring") 之类的东西不会飞。相反,有一些函数可以从列表转换到列表。为什么做出这个选择?在我看来 Haskell 是最好的方式,字符列表和字符串之间在任何方面都没有区别。我最喜欢这个,因为它以最清晰、最简单的方式传达了字符串的含义。我不完全确定,但我猜 'background' 字符串中几乎是任何语言的列表或字符数组。我特别期待一种像 scheme 这样专注于简单性的语言以这种方式处理字符串,或者至少 make 这样你就可以像处理列表一样处理字符串,比如使用 carcdr 我错过了什么?

从技术上讲,"array" 通常是一块连续的内存,而 "list" 通常被理解为独立分配对象的单个或 double-linked 列表。在大多数常见的编程语言中,包括 C 以及我所知道的所有 Lisp 和 Scheme 方言,出于性能原因,字符串数据存储在一个数组中,也就是说它存储在一个连续的内存中。

令人困惑的是,有时它们可​​能仍通俗地称为列表,这在将 "list" 理解为精确的技术术语 "linked list" 时是不正确的。

如果一个字符串真正存储为列表,包括 Scheme 和 Lisp 通常如何存储一个字符串,每个字符都会有成为包含该字符和至少一个指向下一个字符的指针的对象的一部分的开销.

根据 Racket 文档,字符串 个字符数组:

4.3 Strings

A string is a fixed-length array of characters.

数组,这个术语通常用在编程语言中,尤其是在 C 和 C++ 中,是一个连续的内存块,具有重要的 属性它支持高效的随机访问。例如,您可以像访问第 nth (x[n-1]) 一样快地访问第一个元素 (x[0])。链表,你在Lisps中默认遇到的列表,支持高效的随机访问。

因此,由于字符串在 Racket 中是数组,因此您会期望有一些与 x[i] 符号相对应的符号(这不是很 Lispy)。在 Racket 中,您使用 string-refstring-set!,它们记录在同一页面上。例如:

(string-ref "mystring" 1) ;=> #\y 

(现在,还有 vector-refvector-set! 程序用于更广义的向量。在一些Lisps,字符串也是向量,所以你可以在字符串上使用一般的向量和数组操作函数。我不是一个Racket用户,所以我不确定这是否也适用于Racket。)

看来您真正要问的是,为什么没有同时适用于字符串和列表的通用操作?

那些确实存在,在像 generic collections 库这样的库中。

#lang racket/base
(require data/collection)
(first '(my list)) ; 'my
(first "mystring") ; #\m

此外,此库中的 map 等操作可以同时处理多种不同类型的集合。

#lang racket/base
(require data/collection)
(define (digit->char n)
  (first (string->immutable-string (number->string n))))
(first (map string (map digit->char '(1 2 3)) "abc"))
; "1a"

这并不意味着字符串是列表,但确实意味着字符串和列表都是序列,并且对序列的操作可以同时作用于两种数据类型.