Skip to content

Commit 0324fa9

Browse files
dev: 调整章节 (#31)
* dev: 调整章节 * dev: 调整章节2 * dev: 调整章节3 * dev: 调整章节4 * dev: 调整章节5 * dev: 调整章节6 * fix: random style * dev: rearrange * dev: rearrange 2 * dev: rearrange 3 * dev: rearrange 4 * dev: rearrange 5 * dev: rearrange 6 * feat: 编辑排版Ⅰ和脚本Ⅰ * feat: 继续修改顺序 * dev: commit staged
1 parent d52bdcf commit 0324fa9

29 files changed

+3665
-3608
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ dist/
22
target/
33
.DS_Store
44
src/*.pdf
5+
.idea

.vscode/settings.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
{
2-
"typst-preview.invertColors": "auto",
3-
"tinymist.fontPaths": [
4-
"${workspaceFolder}/assets/fonts",
5-
"${workspaceFolder}/assets/typst-fonts"
6-
],
2+
"tinymist.fontPaths": ["${workspaceFolder}/assets/fonts"],
73
"tinymist.exportPdf": "onSave",
84
"tinymist.outputPath": "$root/target/$dir/$name",
95
"tinymist.formatterMode": "typstyle",
@@ -12,6 +8,9 @@
128
"typst.link.image",
139
"typst.link"
1410
],
11+
"tinymist.preview.invertColors": "auto",
12+
"tinymist.lint.enabled": true,
13+
"tinymist.lint.when": "onSave",
1514
"[typst]": {
1615
"editor.inlayHints.enabled": "off"
1716
},

_typos.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[default]
2+
extend-ignore-identifiers-re = ["typ", "typc", "typm"]

src/basic/scripting-access.typ

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#import "mod.typ": *
2+
3+
#show: book.page.with(title: "成员与方法")
4+
5+
// == 复合字面量
6+
7+
// ,它们是:
8+
// + #term("array literal", postfix: "。")
9+
// + #term("dictionary literal", postfix: "。")
10+
11+
Typst提供了一系列「成员」和「方法」访问字面量、变量与函数中存储的“信息”。
12+
13+
其实在上一节(甚至是第二节),你就已经见过了「成员」语法。<grammar-member-exp>你可以通过「点号」获得代码块的“text”(文本内容):
14+
15+
#code(```typ
16+
#repr(`OvO`.text), #type(`OvO`), #type(`OvO`.text)
17+
```)
18+
19+
每个类型有哪些「成员」是由Typst决定的。你需要逐渐积累经验以知晓这些「成员」的分布,才能更快地通过访问成员快速编写出收集和处理信息的脚本。(todo: 建议阅读《参考:XXX》)
20+
21+
当然,为防你不知道,大家不都是死记硬背的:有软件手段帮助你使用这些「成员」。许多编辑器都支持LSP(Language Server Protocol,语言服务),例如VSCode安装Tinymist LSP。当你对某个对象后接一个点号时,编辑器会自动为你做代码补全。
22+
23+
#figure(image("./IDE-autocomplete.png", width: 120pt), caption: [作者使用编辑器作代码补全的精彩瞬间。])
24+
25+
从图中可以看出来,该代码片段「对象」上有七个「成员」。特别是“text”成员赫然立于其中,就是它了。除了「成员」列表,编辑器还会告诉你每个「成员」的作用,以及如何使用。这时候只需要选择一个「成员」作为补全结果即可。
26+
27+
== 方法 <grammar-method-exp>
28+
29+
「方法」是一种特殊的「成员」。准确来说,如果一个「成员」是一个对象的函数,那么它就被称为该对象的「方法」。
30+
31+
来看以下代码,它们输出了相同的内容,事实上,它们是*同一*「函数调用」的不同写法:
32+
33+
#code(```typ
34+
#let x = "Hello World"
35+
#let str-split = str.split
36+
#str-split(x, " ") \
37+
#str.split(x, " ") \
38+
#x.split(" ")
39+
```)
40+
41+
第三行脚本含义对照如下。之前已经学过,这正是「函数调用」的语法:
42+
43+
```typ
44+
#( str-split( x, " " ))
45+
// 调用 字符串拆分函数,参数为 变量x和空格
46+
```
47+
48+
与第三行脚本相比,第四行脚本仍然是在做「函数调用」,只不过在语法上更为紧凑。
49+
50+
第五行脚本则更加简明,此即「方法调用」。约定`str.split(x, y)`可以简写为`x.split(y)`,如果:
51+
+ 对象`x``str`类型,且方法`split``str`类型的「成员」。
52+
+ 对象`x`用作`str.split`调用的第一个参数。
53+
54+
「方法调用」即一种特殊的「函数调用」规则(语法糖),在各编程语言中广泛存在。其大大简化了脚本。但你也可以选择不用,毕竟「函数调用」一样可以完成所有任务。
55+
56+
#pro-tip[
57+
这里有一个问题:为什么Typst要引入「方法」的概念呢?主要有以下几点考量。
58+
59+
其一,为了引入「方法调用」的语法,这种语法相对要更为方便和易读。对比以下两行,它们都完成了获取`"Hello World"`字符串的第二个单词的第一个字母的功能:
60+
61+
#code(
62+
```typ
63+
#"Hello World".split(" ").at(1).split("").at(1)
64+
#array.at(str.split(array.at(str.split("Hello World", " "), 1), ""), 1)
65+
```,
66+
al: top,
67+
)
68+
69+
可以明显看见,第二行语句的参数已经散落在括号的里里外外,很难理解到底做了什么事情。
70+
71+
其二,相比「函数调用」,「方法调用」更有利于现代IDE补全脚本。你可以通过`.split`很快定位到“字符串拆分”这个函数。
72+
73+
其三,方便用户管理相似功能的函数。不仅仅是字符串可以拆分,似乎内容及其他许多类型也可以拆分。如果一一为它们取不同的名字,那可就太头疼了。相比,`str.split`就简单多了。要知道,很多程序员都非常头痛为不同的变量和函数取名。
74+
]
75+
76+
== 数组和字典的成员访问
77+
78+
为了访问数组,你可以使用`at`方法。“at”在中文里是“在”的意思,它表示对「数组」使用「索引」操作。`at(0)`索引到第1个值,`at(n)`索引到第 $n + 1$ 个值,以此类推。如下所示:
79+
80+
#code(```typ
81+
#let x = (1, "OvO", [一段内容])
82+
#x.at(0), #x.at(1), #x.at(2)
83+
```)
84+
85+
至于「索引」从零开始的原因,这只是约定俗成。等你习惯了,你也会变成计数从零开始的好程序员。
86+
87+
为了访问字典,你可以使用`at`方法。但由于「键」都是字符串,你需要使用字符串作为字典的「索引」。
88+
89+
#code(```typ
90+
#let cat = (attribute: [kawaii\~])
91+
#cat.at("attribute")
92+
```)
93+
94+
为了方便,Typst允许你直接通过成员方法访问字典对应「键」的值: <grammar-dict-member-exp>
95+
96+
#code(```typ
97+
#let cat = ("attribute": [kawaii\~])
98+
#cat.attribute
99+
```)
100+
101+
== 数组和字典的「存在谓词」
102+
103+
为了访问数组,你可以使用`contains`方法。“contain”在中文里是“包含”的意思,如下所示:
104+
105+
#code(```typ
106+
#let x = (1, "OvO", [一段内容])
107+
#x.contains[一段内容]
108+
```)
109+
110+
因为这太常用了,typst专门提供了`in`语法,表示判断`x`是否*存在于*某个数组中:<grammar-array-in>
111+
112+
#code(```typ
113+
#([一段内容] in (1, "OvO", [一段内容])) \
114+
#([另一段内容] in (1, "OvO", [一段内容]))
115+
```)
116+
117+
字典也可以使用此语法,表示判断`x`是否是字典的一个「键」。特别地,你还可以前置一个`not`判断`x`是否*不在*某个数组或字典中:<grammar-dict-in>
118+
119+
#code(```typ
120+
#let cat = (neko-mimi: 2)
121+
#("neko-kiki" not in cat)
122+
```)
123+
124+
注意:`x in (...)``"x" in (...)`是不同的。例如`neko-mimi in cat`将检查`neko-mimi`变量的内容是否是字典变量`cat`的一个「键」,而`"neko-mimi"`检查对应字符串是否在其中。
125+
126+
== 总结
127+
128+
// Typst如何保证一个简单函数甚至是一个闭包是“纯函数”?
129+
130+
// 答:1. 禁止修改外部变量,则捕获的变量的值是“纯的”或不可变的;2. 折叠的对象是纯的,且「折叠」操作是纯的。
131+
132+
// Typst的多文件特性从何而来?
133+
134+
// 答:1. import函数产生一个模块对象,而模块其实是文件顶层的scope。2. include函数即执行该文件,获得该文件对应的内容块。
135+
136+
// 基于以上两个特性,Typst为什么快?
137+
138+
// + Typst支持增量解析文件。
139+
// + Typst所有由*用户声明*的函数都是纯的,在其上的调用都是纯的。例如Typst天生支持快速计算递归实现的fibnacci函数:
140+
141+
// #code(```typ
142+
// #let fib(n) = if n <= 1 { n } else { fib(n - 1) + fib(n - 2) }
143+
// #fib(42)
144+
// ```)
145+
// + Typst使用`include`导入其他文件的顶层「内容块」。当其他文件内容未改变时,内容块一定不变,而所有使用到对应内容块的函数的结果也一定不会因此改变。
146+
147+
// 这意味着,如果你发现了Typst中与一般语言的不同之处,可以思考以上种种优势对用户脚本的增强或限制。
148+
149+
#todo-box[总结]
150+
151+
== 习题
152+
153+
#todo-box[习题]

0 commit comments

Comments
 (0)