1 Skill 的语言特征
Skill 是一门高级交互编程语言,基于 Lisp 语言开发,同时也支持类 C 语言语法,但是不建议使用类 C 风格写法,有些地方用 C 风格的很别捏。发展至今,Skill 不仅支持函数式编程,还支持命令式编程,而且面向对象编程也是作了支持。
1.1 程序即数据
这是 Skill 最重要的一个特征,在 Skill 中程序和数据一样被表示为列表,所以它们就像数据一样可以被操作。我们可以动态地创建或修改列表,比如一个函数可以反复被重定义,进而影响到使用这个函数的地方,这种"程序即数据"的概念使得 Skill 可以用来编写灵活的应用程序。
1.2 Skill 的表达式
既然程序即数据,那它们就一定得有共同的表示形式,在 Skill 中也称作 S 表达式,有两种:
- 原子
原子是一个单独的值,可以是数字、字符串、符号等,比如
ini
123 ;数字
"hello" ;字符串
Symbol ;符号
- 列表
列表是由一对圆括号包围的一系列表达式,其中第一个元素通常是函数或宏的名字,后面的元素是函数的参数。列表元素间用空格隔开。列表可以彼此嵌套使用,这也是 Skill 灵活语法的直观体现。
scss
(list 1 2 3) ; 创建列表 (1 2 3)
(cons 1 (list 2 3)) ; 创建列表 (1 2 3)
在上面这段代码里,list 是一个函数名称,用于创建列表;cons 也是一个函数,用来连接两个列表。
1.3 Skill 的函数调用
在 Skill 中可以像 C 语言那样调用函数,也可以使用 Skill 的 S 表达式形式。例如现在有一个函数 add,接收两个数字返回相加后的结果。
- C语言风格
scss
add(1 5) ; 返回6
- S 表达式风格
csharp
(add 1 5) ; 返回6
Skill 中,函数默认返回函数体中最后一个表达式的值,如果最后一个表达式无返回值,则返回 nil,nil 表示空值。
2 特殊符号
Skill 中有一些符合具有特殊函数,且不同于常见的语言(如 C 和 Java)。
2.1 括号 ()
在 Skill 中括号代表一个列表表达式,要求第一个参数是函数,所以不能像在 C 语言里面那样随意加。如下图所示,我们给数字 1 加括号,就会报错。
给数字 1 加括号
如果想使用括号定义列表,除了使用 list 函数外,还可以使用单引号 '。
scss
'(1 2 3) ; 创建列表 (1 2 3)
2.2 单引号 '
Skill 中的单引号有使用原本值的意思,类似于引用的感觉。可用于创建列表及创建符号变量以及引用函数。比如
ini
'a ; 创建符合 a
'abc ; 创建符合 abc
(fboundp 'add) ; 这里的单引号就是引用函数 add 的意思
2.3 分号 ;
分号用于单行注释,我在之前已经有使用过,多行注释的形式则跟其他语言类似,使用 /* ... */ 的形式。
2.4 空格
空格用于Skill 中的元素分割。
2.5 大括号 {}
大括号把多个表达式组合成一个表达式,返回值是最后一个表达式的返回值,可以减少不必要的返回值。如图所示,下面的代码只有一个返回值 5。
大括号的使用
2.6 t
t 表示布尔值 true,false 使用 nil。此外,所有的非 nil 值都会被认为是 true,尤其要注意 0 也是 true,这与 C++ 中很不同。
2.7 中括号 []
中括号用于数组的下标访问。
3 变量及变量类型
前文我们多次提到符号、变量之类的词,这里就说说 Skill 中的变量。
变量在 Skill 中叫做 Symbol,是动态类型,使用前不需要先声明,跟 python 很像。
Skill 中的变量默认是全局变量,局部变量需要特别声明,尤其要注意这点,否则很容易改变全局变量的值,造成逻辑错误。
3.1 变量类型
- 符号 symbol
symbol 既是刚刚说到的变量,又代表一种数据类型,类似 char 类型的变量。
第一次 a 的类型是 symbol
- 字符串 string
是的,就是我们熟知的字符串,无需多言。
- 整数 integer
整数。
- 浮点数 float
浮点数。
- 列表 list
列表是一种数据结构,也可以说是一种变量类型,这里我们就傻傻不分清,在 Skill 中连程序都是数据,数据结构其实没那么严格。
使用 list 函数或者单引号创建列表。列表对元素个数、元素类型没有限制,可以任意嵌套。要注意,使用单引号定义 list 时,不会对内部的表达式进行展开求值。
使用单引号 ' 定义list
还可以使用冒号定义只有两个元素的 list,常用于定义坐标。
ini
a = ( 1 : 4 ) ;定义列表 (1 4)
- 数组 array
使用 declare 定义数组:
ini
(declare week[7]) ; 定义一个含7个元素的数组
数组元素使用 [] 的形式进行访问,下标从 0 开始。数组可以直接使用等会赋值,是浅拷贝。
- 关联表 Association Tables
以 key/value 的形式保存的数据的集合,key 的类型没有限制,可以是任意类型。使用 makeTable 函数创建一个table:
ini
myTable = (makeTable "table1" 0)
"table1"是表名,并不是变量名称,0 表示从表中查不到 key 时返回 0。
3.2 局部变量
- 使用 let 定义局部变量
不声明初始值的格式如下,定义了 a,b 两个局部变量。
使用 let 定义局部变量
声明初始值的格式,如下,局部变量 a 的初始值是 7,b 的初始值是 5.
使用 let 定义局部变量并赋初始值
- 使用 prog 定义局部变量
prog 增加了对 return 和 go 的支持,要注意,prog 和 循环一起使用时,return 始终是返回到 prog 的外围,prog 没有赋初始值的语法。
3.3 局部变量的特性
局部变量能穿透到函数内部,就跟全局变量一样。
下图定义了两个函数 trOne 和 trTwo,trOne 先定义了局部变量 aGlobal 然后调用了函数 trTwo;trTwo 函数第一行会打印 aGlobal 的值。
局部变量穿透到函数内部
如果我们调用 trOne,发现内部的 trTwo 函数里第一行的 aGlobal 是 trOne 里定义的局部变量,值为 5;如果我们在 trOne 外面直接调用 trTwo,此时访问的是全局变量 aGlobal。