系列文章目录
开胃小菜——cons和pair
在 Fift 语言中,元组(Tuples)和列表(Lisp-style lists)是两种不同的数据结构,但只是语义上的不同,实际上两者底层是互通的。
元组是不可变的有序集合,可以包含任意数量和类型的值。在 Fift 中,元组的创建使用 tuple
操作,而访问元组中的元素则使用 untuple
操作(在你的描述中没有提到,但是它是处理元组的基本操作之一)。例如,创建一个包含两个元素的元组(也就是一个对):
2 3 pair
这将创建一个元组 (2, 3)
。
列表是 Fift 中用来表示序列的一种数据结构,它是由一系列元素组成的,每个元素都可以是任意类型。列表的末尾使用一个特殊的值 null
来表示结束,这是 Lisp 语言中的传统。在 Fift 中,列表的操作包括 cons
(构造一个新的列表,将一个元素添加到另一个列表的前面),uncons
(分解列表为头和尾),car
(获取列表的第一个元素),cdr
(获取列表的剩余部分),以及 list
(创建一个新列表)。在 Fift 中,cons
和 uncons
被定义为 pair
和 unpair
的别名,这意味着它们在功能上是等价的。但是,它们在语义上用于不同的数据结构:pair
和 unpair
用于元组,而 cons
和 uncons
用于列表。
尽管 pair
和 cons
在功能上是等价的,但它们在语义上用于不同的数据结构。在 Fift 中,这种区分是通过上下文来实现的。当你使用 pair
时,你通常在处理元组;而当你使用 cons
时,你通常在处理列表。这种约定帮助程序员理解代码的意图,并且避免了操作的冲突。
示例代码
"main" 0 pair @proclist @ cons @proclist !
这段代码首先使用 pair
创建了一个包含字符串 "main"
和数字 0
的元组,然后使用 cons
将这个元组添加到一个名为 @proclist
的列表中。这里没有冲突,因为 pair
和 cons
在这里用于不同的上下文:pair
用于创建元组,而 cons
用于将元组添加到列表中。
但对于我们熟悉底层代码的人,我们知道这样的代码中pair和cons是可以随意互换的。
"main" 0 pair .s @proclist @ .s pair .s @proclist !
"main" 0 cons .s @proclist @ .s pair ,s @proclist !
"main" 0 cons .s @proclist @ .s cons ,s @proclist !
以上几种情况都是等价的输出
[ "main" 0 ]
[ "main" 0 ] (null)
[ [ "main" 0 ] (null) ]
一、main函数的信息
"main" 0 @proclistadd
dictnew dup @procdict !
@procinfo ! 16 0 @procinfo!
} : PROGRAM{
这是main函数的基本信息,我们可以看出对于他来说@procdict @procinfo 都是空字典
在 @proclist 中放入了,(“main” 0)的元组,这便是一切程序的初始阶段 。接下来详细讲解16 0 @procinfo!
二、@procinfo!
{ <b rot 16 i, swap @procinfo @ @procdictkeylen b>idict!
not abort"cannot add key to procedure info dictionary"
@procinfo !
} : @procinfo!
-
<b
:这部分代码首先使用<b
开始一个构建器build,在 Fift 语言中,<b 和 b> 是两个用于操作构建器(Builder)的原语(primitives)。构建器是一种可以动态构建数据结构的工具,它允许你逐步添加元素,最后生成一个新的单元(Cell)。
<b:这个操作会创建一个新的空构建器,并将其推入栈中。构建器是一种可以容纳一系列数据的临时存储区域,你可以向其中添加数据,但还不能形成一个完整的单元。这个操作通常用于开始构建一个新的数据结构。b>:这个操作接受一个构建器作为输入,并将其转换成一个新的单元(Cell),这个单元包含了构建器中所有的数据。这个操作通常用于完成构建过程,将构建器中的所有数据固化到一个新的单元中。 -
rot
操作,这通常用于旋转栈顶的三个元素的位置。
1 2 3 rot -> 2 3 1
故 16 0 <b rot
0 <b 16
-
16 i,
这里也是fift最麻烦的地方 储存类型的以及字节大小的相关定义,如i
(b x y – b′)
- 这个操作用于将一个有符号的
y
位整数x
以大端序二进制形式追加到构建器b
中。 x
的范围是从-2^(y-1)
到2^(y-1)-1
,其中0 ≤ y ≤ 257
。- 如果构建器
b
中没有足够的空间来存储这个整数(即如果b
已经包含超过1023 - y
个数据位),或者整数x
不能用y
位表示,则会抛出一个异常。 b′
是修改后的构建器,包含了追加的整数。
u,
(b x y – b′)
- 这个操作用于将一个无符号的
y
位整数x
以大端序二进制形式追加到构建器b
中。 x
的范围是从0
到2^y-1
,其中0 ≤ y ≤ 256
。- 如果操作不可能执行(例如,由于空间不足或整数大小不合适),则会抛出一个异常。
b′
是修改后的构建器,包含了追加的整数。
ref,
(b c – b′)
- 这个操作用于将单元
c
的引用追加到构建器b
中。 - 如果
b
已经包含了四个引用,则会抛出一个异常,因为构建器通常限制了可以存储的引用数量。 b′
是修改后的构建器,包含了追加的引用。
s,
(b s – b′)
- 这个操作用于将切片
s
中的数据位和引用追加到构建器b
中。 s
是一个包含数据位和引用的切片(Slice)。b′
是修改后的构建器,包含了追加的数据和引用。
sr,
(b s – b′)
- 这个操作用于从切片
s
构建一个新的单元,并将这个新单元的引用追加到构建器b
中。 - 这相当于先使用
<b
创建一个新的空构建器,然后使用s
中的数据和引用填充这个构建器,接着使用b>
将填充好的构建器转换成一个新的单元,最后将这个新单元的引用追加到原始构建器b
中。 b′
是修改后的构建器,包含了新单元的引用。
$,
(b S – b′)
- 这个操作用于将字符串
S
追加到构建器b
中。 - 字符串
S
被解释为一个长度为8n
的二进制字符串,其中n
是S
的 UTF-8 表示中的字节数。 - 这意味着每个 UTF-8 字节都被转换为一个 8 位的二进制表示,并追加到构建器中。
b′
是修改后的构建器,包含了追加的字符串。
所以,我们的程序就是要装入一个16位的有符号整数x,2^ 15 < x <2^ 15
-
not abort"cannot add key to procedure info dictionary"
:如果idict!
操作失败(即返回了0
),则执行abort
操作,这是一个错误处理操作,用于在执行失败时终止程序,并输出错误信息 “cannot add key to procedure info dictionary”。 -
接下来我们来详细描述一下字典的封闭元语
在 Fift 语言中,字典(dictionary)是一种重要的数据结构,用于存储键值对。以下是您提到的几个与字典操作相关的原语(primitives)的详细解释:dictnew
( – D)
- 这个操作会创建一个新的空字典,并将其作为 Null 值推入栈中。这个空字典随后可以被用来添加键值对。
idict!
(v x D n – D′ −1 or D 0)
- 这个操作会将一个由切片表示的新值
v
和一个有符号的大端序n
位整数键x
添加到字典D
中。 - 如果操作成功,返回新的字典
D′
和-1
作为成功的标志。 - 如果操作失败(例如,如果键已存在或字典空间不足),则返回未改变的字典
D
和0
。 n
位键的长度,其中0 ≤ n ≤ 1023
。
idict!+
(v x D n – D′ −1 or D 0)
- 这个操作与
idict!
类似,但它在尝试添加键值对时,如果键已经存在于字典中,则会失败,返回未改变的字典D
和0
。
b>idict!
,b>idict!+
- 这些操作是
idict!
和idict!+
的变体,它们接受构建器(Builder)中的新值v
而不是切片。 - 构建器是一种临时存储区域,可以动态构建数据结构,如字典。这些操作允许你将构建器中的数据直接添加到字典中。这些操作提供了一种灵活的方式来操作字典,允许程序员在 Fift 程序中存储和管理键值对。字典在 Fift 中通常用于存储配置数据、状态信息或其他需要快速查找的数据结构。
在实际使用中,这些操作可能会涉及到对字典的序列化和反序列化,以及对字典中键值对的遍历和查询。例如,你可以使用idict!
来添加一个新的键值对到字典中,或者使用idict!+
来确保不会覆盖已有的键。如果需要将构建器中的数据添加到字典中,可以使用b>idict!
或b>idict!+
操作。
-
@procinfo !
:如果idict!
操作成功,将更新后的@procinfo
变量的值存储回@procinfo
。
x y <b rot 16 i, swap @procinfo @ @procdictkeylen b>idict!
我们可以看到此时此刻是一个装有16数据x的build作为value,@procdictkeylen长度的y作为key,他们一起被放入dict 字典中。
- 最后的处理
not abort"cannot add key to procedure info dictionary" @procinfo !
这段代码的目的是尝试将一个新的键值对添加到 @procinfo
字典中。如果添加失败(可能是因为键已存在),则程序将终止并输出错误信息。如果添加成功,则更新 @procinfo
变量的值。