Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

23 Feb 2021

The Racket Guide阅读笔记【二】

The Racket Guide 阅读笔记,chapter 3.

3 Built-In Datatypes

3.1 Booleans

3.2 Numbers

3.3 Characters

Racket字符可以对应到一个Unicode标量。这个标量是一个21位整型。通过char->integer以及integer->char可以在整型和字符之间互相转化。

字符常量默认的打印暑促以#\为前缀。如果使用display来打印,就不会加上那个前缀。

3.4 Strings (Unicode)

字符串是具有一定长度的字符序列。字符串可以是可变化的或者不可变的。常量形式的字符串是不可变的。通过make-string生成的字符串可以通过string-set!修改。

字符串提供一些支持本地化的操作,例如:

  • string-locale<?
  • string-locale-ci<?

3.5 Bytes and Byte Strings

字节是一个值从0到255的整型。判定诀byte?可以用来将整型识别为字节。

字节串和字符串类似,不过其单位是字节而不是字符。字节串的写法如#"Apple"

display字节串的时候不会做转码,具体显示成什么样子取决于终端的编码。

一些字节串和字符串之间的转换示例:

  • (bytes->string/utf-8 #"\316\273")
  • (bytes->string/latin-1 #"\316\273")

3.6 Symbols

符号名是编译器识别的字符串,形如'a。除了 ( ) [ ] { } " , ' `` ; # | \之外的字符可以直接以常量的形式写做符号名,否则要通过 string->symbol来转化。

Actually, # is disallowed only at the beginning of a symbol, and then only if not followed by %; otherwise, # is allowed, too. Also, . by itself is not a symbol. Whitespace or special characters can be included in an identifier by quoting them with | or .

write函数打印符号名的时候不会带',display也一样。

gensym生成随机不重复的符号名。string->uninterned-symbol可以生成跟以外任何符号名都不重复的符号名,即便内容相同,也就是说(eq?, eqv?, equal?都返回#t)

3.7 Keywords

关键字跟符号名点像,但是打印出来是以#:为前缀。更准确的说,关键字有点像标识符,可以升格(quote)成符号名。string->keyword可以将字符串转为关键字。非升格(unquote)的关键字就像非升格的标识符,它们不是表达式,无法产生符号名。

下面两个例子都不是表达式:

  • not-a-symbol-expression
  • #:not-a-keyword-expression

关键字常用于函数调用参数列表以及一些其他语法形式内。

3.8 Pairs and Lists

list* is used to abbreviate a series of conses that cannot be abbreviated using list

例子:

> (cons 1 (cons 2 (srcloc "file.rkt" 1 0 1 8)))
(list* 1 2 (srcloc "file.rkt" 1 0 1 8))

write或者display打印对子或者连对的时候,不会添加额外的'listlist*之类的辅助修饰符。

Racket中的对子是非可变数据类型(跟传统的lisp不一样)。pair?list?只能用于非可变对子。mcons构成的对子是可变的,可以使用set-mcar!以及set-mcdr!来修改。可变list上使用mcarmcdr来获取对子头和对子尾。可变连对打印的时候会用mcons修饰,通过writedisplay打印的时候会用{ }表示。

3.9 Vectors

串列(vector)是一个定长数组,其元素可以是任意类型数据。其常量表示方式跟连对有点像,不过需要使用#前缀,后面可以跟串列长度,例如:#4(baldwin bruce)会产生值'#(baldwin bruce bruce bruce)

串列跟连对可以互相转化:vector->list以及list->vector。和连对一样,常量串列是不可改的,只有动态创建的串列才具有可修改性。

3.10 Hash Tables

hash, hasheqv, hasheq从初始值序列中创建不可改的哈希表。make-hash则用来创建可修改的哈希表。可以使用#hash#hasheq, #hasheqv来创建一个常量哈希表。

可修改的哈希表可以采用弱引用的方式来处理键值的留存,例子如下:

> (define ht (make-weak-hasheq))
> (hash-set! ht (gensym) "can you see me?")
> (collect-garbage)
> (hash-count ht)
0

如果哈希表中的键和值互相引用,可能会导致其无法释放,一个解决办法是采用ephemeron:

> (define ht (make-weak-hasheq))
> (let ([g (gensym)])
    (hash-set! ht g (list g)))
> (collect-garbage)
> (hash-count ht)
1

> (define ht (make-weak-hasheq))
> (let ([g (gensym)])
    (hash-set! ht g (make-ephemeron g (list g))))
> (collect-garbage)
> (hash-count ht)
0

3.11 Boxes

一个盒子就像是一个单元素的串列。其常量前缀是#&。例子:

> (define b (box "apple"))
> b
'#&"apple"

> (unbox b)
"apple"

> (set-box! b '(banana boat))
> b
'#&(banana boat)

3.12 Void and Undefined

一些表达式或者执行诀其实是不需要有返回值的。比如执行诀display的调用目的是为了产生副作用,而不是为了求得结果。这种情况下,返回值是#<void>

执行诀void可以接受任意个数的参数并返回#<void>

undefined常量,打印形式为#<undefined>。用的地方较少,只在share形式的某些案例下使用。

(本篇完)

Categories