Factor 语言概览
摘要
面向刚开始接触栈式语言的开发者,以代码为主导地介绍 Factor 编程语言,涵盖基本语法、栈行为、控制流、quotation、对象、元编程以及核心库。
<p><a href="https://lobste.rs/s/bwtzhy/factor_overview">评论</a></p>
查看缓存全文
缓存时间: 2026/10/05 13:12
# Factor 概述 – Re: Factor 来源:https://re.factorcode.org/2026/10/factor-overview.html
我强烈建议你阅读[Factor 导览](https://docs.factorcode.org/content/article-tour.html)。它对这门语言以及 Factor(https://factorcode.org/)的库做了很好的介绍。尽管如此,有时我还是希望有更偏重代码的示例,涵盖日常语法、控制流、组合子,以及一些主要的库。这篇概述就是为此而写的。它假定你之前写过程序,但不一定用过基于栈的语言。
### Hello, world
最简单的 [Hello, world](https://en.wikipedia.org/wiki/Hello,_world) 就是:
```
"Hello, world!" print
```
你可以在 [listener](https://docs.factorcode.org/content/article-listener.html) 中运行它:
```
IN: scratchpad
"Hello, world!" print
Hello, world!
```
也可以从命令行运行:
```
$ ./factor -e="\"Hello, world!\" print"
Hello, world!
```
当然,你也可以把它写成一个名为 `hello.factor` 的文件,其中定义了一个 `hello` 词汇表(vocabulary,这是你在[你的第一个程序](https://docs.factorcode.org/content/article-first-program.html)教程中会了解到的东西)。
```
USING: io ;
IN: hello
: main ( -- )
"Hello, world!" print ;
MAIN: main
```
上面用到的[语法](https://docs.factorcode.org/content/article-syntax.html)包括:
- [`USING:`](https://docs.factorcode.org/content/word-USING__colon__%2Csyntax.html) 用于导入词汇表(命名的 word 集合),
- [`IN:`](https://docs.factorcode.org/content/word-IN__colon__%2Csyntax.html) 用于选择定义所在的词汇表,
- [`MAIN:`](https://docs.factorcode.org/content/word-MAIN__colon__%2Csyntax.html) 用于设置入口点。
之后你可以把它当作脚本来运行:
```
$ ./factor hello.factor
Hello, world!
```
或者,如果该文件位于[词汇表根路径](https://docs.factorcode.org/content/article-vocabs.roots.html)的搜索路径中,可以直接运行该词汇表的 main word:
```
$ ./factor -run=hello
Hello, world!
```
在本概述的其余部分,请在 *listener*,即 Factor 的交互式 [REPL](https://docs.factorcode.org/content/article-listener.html) 中尝试这些示例。用 `\./factor -run=listener` 启动终端 listener,或者在开发环境中使用图形化 listener。每个示例都包含其导入;建立在某个定义之上的示例假定你已经输入过该定义。`IN: scratchpad`(https://docs.factorcode.org/content/word-IN__colon__%2Csyntax.html)会把实验性的定义放进 listener 通常使用的工作词汇表中。你可以直接粘贴代码,看看它做了什么。以 `\!` 开头的注释是合法的 Factor 源码的一部分。
为了快速上手,请依次学习栈、word 定义、quotation(代码引用)、控制流和序列。后面的章节会介绍对象、元编程和各种库,你可以按需回头查阅。
### 值与栈
[字面量](https://docs.factorcode.org/content/article-literals.html)会把值压入数据栈。word 从栈顶取出输入,并压出它的输出。代码从左向右执行:
```
USING: math prettyprint ;
2 3 + . ! 5
10 4 - . ! 6
2 3 + 4 * . ! 20
```
[`.`](https://docs.factorcode.org/content/word-.%2Cprettyprint.html) 取出并打印一个对象。[`print`](https://docs.factorcode.org/content/word-print%2Cio.html) 取出并打印一个字符串。示例旁边的注释显示的是输出。由于打印会移除该值,这些示例在没有特别说明的情况下会让栈变为空。
函数参数周围没有括号:把参数放到栈上,然后调用该 word。下面的示例中,栈顶在右侧:
```
代码 栈
2 2
3 2 3
+ 5
4 5 4
* 20
. (空)
```
空格很重要。`2 3 +` 是三个 token;`2+3` 是一个 token,那它就得是某个 word 的名字。像 [`number>string`](https://docs.factorcode.org/content/word-number__gt__string%2Cmath.parser.html)、[`empty?`](https://docs.factorcode.org/content/word-empty__que__%2Csequences.html) 和 [`set-at`](https://docs.factorcode.org/content/word-set-at%2Cassocs.html) 这样的名字都是普通的 word 名。结尾的 `?` 通常表示这是一个谓词;`>` 常出现在转换相关的名字中。这些字符是名字的一部分,而不是独立的运算符。结尾的 `!` 通常表示可变(mutation)版本,例如 [`append!`](https://docs.factorcode.org/content/word-append%21%2Csequences.html);`*` 通常表示另一种形式。有一些[命名约定](https://docs.factorcode.org/content/article-conventions.html)对学习 word 和类型的命名很有帮助。
### 注释与字面量
```
USING: math multiline prettyprint ;
! 一条注释一直延续到行尾。
/* 块注释可以跨多行。 */
42 . ! 整数
-17 . ! 负整数
0xff . ! 255,十六进制
0b1010 . ! 10,二进制
3/4 . ! 精确有理数
1.25 . ! 浮点数
C{ 2 3 } . ! 复数:2 + 3i
t . ! 真
f . ! 假
"hello\nworld" . ! 带转义的字符串
CHAR: A . ! 65,一个字符码点
{ 1 2 3 } . ! 数组
V{ 1 2 3 } . ! 可增长向量
B{ 0 127 255 } . ! 字节数组
H{ { "name" "Ada" } } . ! 哈希表
[ 1 + ] . ! quotation:作为值的代码
```
数组和 quotation 只包含对象,而不执行它们。集合字面量适合用于固定数据;当在 word 内部要修改其中一个时,用 [`clone`](https://docs.factorcode.org/content/word-clone%2Ckernel.html) 获取一份新的副本,而不要修改共享的字面量。这是浅拷贝:集合内部的对象仍然是共享的。
块注释来自 [`multiline`](https://docs.factorcode.org/content/vocab-multiline.html) 词汇表。
[`CHAR:`](https://docs.factorcode.org/content/word-CHAR__colon__%2Csyntax.html) 产生一个整数码点;Factor 没有单独的字符类型。`{ ... }` 是数组,而 `[ ... ]` 是作为值持有的可执行代码,称为 [quotation](https://docs.factorcode.org/content/article-quotations.html)。字面量的起始符、内容和结束定界符之间用空格分隔,例如 `{ 1 2 3 }` 和 `[ 1 + ]`。
### 字符串与转义字符
字符串字面量使用双引号。反斜杠引入[字符转义](https://docs.factorcode.org/content/article-escape.html):
| 转义 | 含义 |
| --- | --- |
| `\"` | 双引号 |
| `\\` | 反斜杠 |
| `\a` | 响铃(码点 7) |
| `\b` | 退格(8) |
| `\e` | 转义(27) |
| `\f` | 换页(12) |
| `\n` | 换行(10) |
| `\r` | 回车(13) |
| `\s` | 空格(32) |
| `\t` | 制表符(9) |
| `\v` | 垂直制表(11) |
| `\0` | 空字符(0) |
| `\ooo` | 由一到三个八进制数字给出的码点 |
| `\xHH` | 恰好由两个十六进制数字给出的码点 |
| `\uHHHHHH` | 恰好由六个十六进制数字给出的码点 |
| `\u{H...}` | 花括号内由十六进制数字给出的码点 |
| `\u{name}` | 命名的 Unicode 字符,需已加载 Unicode 支持 |
例如:
```
USING: io prettyprint sequences unicode ;
"She said \"hello\"." print ! She said "hello".
"C:\\Users\\Ada" print ! C:\Users\Ada
"\x41\u000042\u{43}" print ! ABC
"\u{greek-small-letter-pi}" print ! π
"first\nsecond" print ! 输出两行
"\t" length . ! 1:该转义代表一个字符
"hello" length . ! 5
"hello" >upper . ! "HELLO"
"a,b,c" "," split . ! { "a" "b" "c" }
{ "a" "b" "c" } ", " join . ! "a, b, c"
"42" string>number . ! 42
42 number>string . ! "42"
"oops" string>number . ! f
```
六位的 `\u` 形式与使用四位的那些语言不同;花括号形式通常更容易阅读。未知的转义是错误。字符串也可以直接跨越源代码行:真正的换行会成为字符串的一部分。紧跟在源代码换行之前的反斜杠会让字符串在不包含该换行的情况下继续。反斜杠后跟一个字面空格也表示一个空格,类似 `\s`。
*注意:[字符串](https://docs.factorcode.org/content/article-strings.html)的长度是码点的数量,而不是可见字形的数量。阅读关于 Factor 的 [Unicode](https://re.factorcode.org/2023/05/unicode.html) 支持的文章可以让你了解得更多。*
### 栈的操作
作为[粘接式语言](https://concatenative.org/wiki/view/Concatenative%20language)的典型特征,栈是一种拥有自身访问模式的数据结构,我们通常称之为 [stack shuffling(栈的操作)](https://docs.factorcode.org/content/article-tour-stack-shuffling.html)。
```
USING: kernel prettyprint ;
10 dup . . ! 打印 10,然后是 10
10 20 swap . . ! 打印 10,然后是 20
10 20 over . . . ! 打印 10,然后是 20,然后是 10
10 20 nip . ! 20:丢弃第二个元素
10 20 drop . ! 10:丢弃栈顶元素
```
常见的[栈操作 word](https://docs.factorcode.org/content/article-shuffle-words.html) 有如下效果:
```
! dup ( x -- x x )
! drop ( x -- )
! swap ( x y -- y x )
! over ( x y -- x y x )
! nip ( x y -- y )
! rot ( x y z -- y z x )
```
大多数 Factor 代码使用简短的定义和组合子,把显式操作栈降到最低。在 stack effect(栈效果)中,输入和输出从左到右排列,最上面的值排在最后。因此 [`swap`](https://docs.factorcode.org/content/word-swap%2Ckernel.html) 会把以 `x y` 结尾的栈变成以 `y x` 结尾;这些输入下方的值不受影响。连续调用 [`.`](https://docs.factorcode.org/content/word-.%2Cprettyprint.html) 会先打印最上面的结果。
### 定义 word
你可以创建包含在被调用时执行的代码的 [word](https://docs.factorcode.org/content/article-words.html):
```
USING: kernel math prettyprint ;
IN: scratchpad
: square ( n -- n-squared )
dup * ;
: neighbors ( n -- below above )
dup 1 - swap 1 + ;
5 square . ! 25
5 neighbors . . ! 打印 6,然后是 4
CONSTANT: answer 42
answer . ! 42
```
[`:`](https://docs.factorcode.org/content/article-colon-definition.html) 开始一个定义,[`;`](https://docs.factorcode.org/content/word-%3B%2Csyntax.html) 结束它。[stack effect](https://docs.factorcode.org/content/article-effects.html) `( inputs -- outputs )` 记录了这个 word 消耗和产生多少个值。其中的名称描述这些值;它们不会绑定变量,也不指定类型。编译器会检查 stack effect,包括分支的 effect 是否兼容。word 可以通过把多个值留在栈上来返回多个值。没有显式的 `return`:执行在 word 末尾结束。
`ALIAS: new-name existing-word`(https://docs.factorcode.org/content/word-ALIAS__colon__%2Csyntax.html)为一个 word 定义另一个名字。
### 算术与比较
[算术](https://docs.factorcode.org/content/article-arithmetic.html)功能很丰富,可用于对[数字](https://docs.factorcode.org/content/article-numbers.html)进行计算:
```
USING: kernel math math.functions math.order prettyprint ;
7 2 / . ! 3+1/2,一个精确有理数
7 2 /i . ! 3,整数除法
7 2 mod . ! 1
2 10 ^ . ! 1024
9 sqrt . ! 3.0
-5 abs . ! 5
3 8 min . ! 3
3 8 max . ! 8
2 3 < . ! t
2 3 >= . ! f
"hello" "hello" = . ! t,值相等
```
[整数](https://docs.factorcode.org/content/article-integers.html)会自动增长到超出机器字长的范围,整数除法可以产生[精确的比值](https://docs.factorcode.org/content/article-rationals.html)。当你想要浮点运算时,请使用浮点数输入。位运算有它们自己的名字,与布尔逻辑分开:
```
USING: math prettyprint ;
0b1100 0b1010 bitand . ! 8
0b1100 0b1010 bitor . ! 14
0b1100 0b1010 bitxor . ! 6
1 3 shift . ! 8:左移
8 -1 shift . ! 4:右移
```
### Quotation
方括号会产生一个 [quotation](https://docs.factorcode.org/content/article-quotations.html)。[`call`](https://docs.factorcode.org/content/word-call,kernel.html) 会执行它:
```
USING: kernel math prettyprint sequences ;
5 [ 1 + ] call . ! 6
{ 1 2 3 } [ 2 * ] map . ! { 2 4 6 }
```
Quotation 可以传给 word、从 word 返回,也可以存入集合。接受 quotation 的 word 称为*组合子*([combinators](https://docs.factorcode.org/content/article-combinators.html))。
### 布尔值与条件
在[布尔测试](https://docs.factorcode.org/content/article-booleans.html)中,只有 [`f`](https://docs.factorcode.org/content/word-f%2Csyntax.html) 为假。零、空字符串和空数组都为真。
```
USING: kernel math prettyprint ;
t f and . ! f
t f or . ! t
f not . ! t
3 2 > [ "yes" ] [ "no" ] if . ! "yes"
0 [ "truthy" ] [ "false" ] if . ! "truthy"
t [ "runs" . ] when
f [ "runs too" . ] unless
```
[`if`](https://docs.factorcode.org/content/word-if,kernel.html) 消耗一个条件和两个 quotation。条件为真时它调用第一个 quotation,为 [`f`](https://docs.factorcode.org/content/word-f%2Csyntax.html) 时调用第二个。[`when`](https://docs.factorcode.org/content/word-when%2Ckernel.html) 和 [`unless`](https://docs.factorcode.org/content/word-unless%2Ckernel.html) 只接受一个 quotation。这些是作用于代码值的 word,就像 [`+`](https://docs.factorcode.org/content/word-%2B%2Cmath.html) 作用于数字一样。
对于多个分支,使用 `cond` 或 `case`(https://docs.factorcode.org/content/article-conditionals.html):
```
USING: combinators kernel math prettyprint ;
IN: scratchpad
: sign-name ( n -- string )
{
{ [ dup 0 < ] [ drop "negative" ] }
{ [ dup 0 = ] [ drop "zero" ] }
[ drop "positive" ]
} cond ;
-3 sign-name . ! "negative"
: color-name ( color -- string )
{
{ "r" [ "red" ] }
{ "g" [ "green" ] }
[ drop "unknown" ]
} case ;
"g" color-name . ! "green"
```
[`cond`](https://docs.factorcode.org/content/word-cond%2Ccombinators.html) 按顺序尝试谓词 quotation。[`case`](https://docs.factorcode.org/content/word-case%2Ccombinators.html) 会把一个输入与每个键比较;匹配的分支会自动消耗该键,而默认分支会接收到未匹配的输入。
[`and`](https://docs.factorcode.org/content/word-and%2Ckernel.html) 和 [`or`](https://docs.factorcode.org/content/word-or%2Ckernel.html) 结合的是已经计算出来的值。要进行[短路求值](https://docs.factorcode.org/content/article-combinators.short-circuit.html),请改为传入谓词 quotation:
```
USING: combinators.short-circuit kernel math prettyprint ;
5 { [ 0 > ] [ 10 < ] } 1&& . ! t:为正且小于十
-5 { [ 0 < ] [ 10 > ] } 1|| . ! t:为负或大于十
```
每个谓词接收相同的输入。[`1&&`](https://docs.factorcode.org/content/word-1%26%26%2Ccombinators.short-circuit.html) 在第一个为假的结果处停止;[`1||`](https://docs.factorcode.org/content/word-1__pipe____pipe__%2Ccombinators.short-circuit.html) 在第一个为真的结果处停止。开头的数字是传给每个谓词的输入数量。
### 保存与隐藏值
[`dip`](https://docs.factorcode.org/content/word-dip,kernel.html) 这个 word 会在 quotation 对其下方的栈操作时,暂时隐藏一个值。[`keep`](https://docs.factorcode.org/content/word-keep,kernel.html) 会把一个值交给 quotation,同时又保留该值:
```
USING: kernel math prettyprint ;
10 20 [ 1 + ] dip + . ! 31:对 10 加一,然后恢复 20
5 [ 1 + ] keep . . ! 打印 5,然后是 6
```
```
! dip ( ..a x quot -- ..b x )
! keep ( ..a x quot -- ..b x )
```
两者的整体形状很相似,但 `dip` 会把 `x` 从 quotation 那里藏起来,而 `keep` 会把它传进去。[`2dip`](https://docs.factorcode.org/content/word-2dip%2Ckernel.html) 隐藏两个值;[`2keep`](https://docs.factorcode.org/content/word-2keep%2Ckernel.html) 保留两个输入。
### 应用多个 quotation
[`bi`](https://docs.factorcode.org/content/article-cleave-combinators.html) 这个家族覆盖了分配输入的几种常见方式:
```
USING: kernel math prettyprint ;
! 将两个 quotation 应用到同一个输入。
5 [ 1 + ] [ 2 * ] bi . . ! 打印 10,然后是 6
! 将一个 quotation 应用到两个输入中的每一个。
3 4 [ 2 * ] bi@ . . ! 打印 8,然后是 6
! 将不同的 quotation 应用到不同的输入。
3 4 [ 1 + ] [ 2 * ] bi* . . ! 打印 8,然后是 4
! 将两个 quotation 应用到同一对输入。
3 4 [ + ] [ * ] 2bi . . ! 打印 12,然后是 7
```
例如,[`2bi`](https://docs.factorcode.org/content/word-2bi%2Ckernel.html) 让一个 word 能从相同的输入算出两个结果:
```
USING: kernel math prettyprint ;
IN: s
相似文章
Multistack Concatenative Programming Languages
An exploration of multistack concatenative programming languages, discussing how auxiliary stacks and dynamic bindings like Factor's namespaces and PostScript's dictionaries can ease data stack management without sacrificing concatenative composition.
从第一原理看函数式编程,第1部分——动机
本文从第一原理介绍函数式编程,涵盖函数的数学定义及编程语言范式的分类。这是面向命令式编程者系列文章的第一部分。
Simon Jones 谈函数式编程、类型思维与无用语言
Simon Peyton Jones 阐述了函数式编程的数学基础、诸如提高可维护性之类的益处,以及其对主流语言的影响,同时讨论了处理副作用等挑战。
Joy 非正式教程
关于 Joy 编程语言的教程,这是一种基于函数组合和组合子的函数式语言,采用后缀表示法和基于栈的执行方式。
F*:一种面向证明的通用编程语言
F* 是一种通用的、面向证明的编程语言,它将依赖类型与基于 SMT 和策略驱动的证明自动化相结合,可编译为 OCaml 及其他目标语言。这是一个由微软研究院、Inria 和社区共同开发的开源项目,用于形式化验证。