键盘快捷键

使用 跳转章节

使用 S/ 在本书内搜索

使用 ? 显示帮助页面

使用 Esc 隐藏帮助页面

KSL 语法说明

概述

KSL 语法受到 Wolfram 语言与 LISP 的启发,采用前缀表示法,所有运算皆通过函数调用完成,不设中缀运算符。

语法围绕三种基本结构展开:基本元素、列表与函数调用。下文逐一说明。

基本元素

KSL 定义了五种基础数据类型:符号、原子、字符串、数字与对象。

符号

符号是变量名与函数名的标识符。

首字符必须为 Unicode 字母或下划线 _。后续字符可为字母、数字、_-@$'?!。以数字或标点开头均不合法。

符号长度上限为 23 个字符。

以下划线开头的符号为模块私有符号,无法被其他文件加载。

示例:aAdd_private-func' 以及 _你好です!

原子

原子是唯一且恒定的标识符,类似枚举值。以冒号 : 开头。

首字符必须为 Unicode 字母,后续字符规则同符号。长度上限同为 23 个字符。

示例::some-tag:你好です!

:true:false 用作布尔值真与假的惯用表示。

字符串

字符串由双引号 " 包围,可跨越多行。引号内的空白、换行与缩进均原样保留。

KSL 不支持 \n 这类转义序列。要嵌入换行等特殊字符,有两种等价方式:使用 :码点 语法直接书写 Unicode 码点,或通过 Chn 函数将码点转为字符后拼接。

Let[s, "Hello, world🌏,
换行直接支持。
  前置空白也会保留。"];

Let[s', Concat[s, :10, "新起一行。"]];
Let[s'', Concat[s, Chn[10], "新起一行。"]];

数字

数字可写为整数、小数或科学计数法。小数点前可省略 0.50.5

当整数与浮点数混合运算时,结果统一为浮点数。

Add[2, 3.0]; # => 6.0
Mul[3, 4.0]; # => 12.0

整数运算不会因溢出而自动提升类型。若需手动转换,可使用 Add[n, 0.0]Mul[n, 1.0]

字符

字符以 :十进制码点 语法书写,直接嵌入 Unicode 字符而无须转义序列。码点最多 8 位十进制数字。

:10     # => "\n"
:65     # => "A"
:128512 # => "😀"

Chn 函数可在字符与码点之间互转:传入单个字符返回其码点,传入码点返回对应字符。

Chn["A"];  # => 65
Chn[65];   # => "A"

对象

对象是带类型标签的键值容器,用于结构化数据。

创建与初始化

Object 函数至少需要一个符号作为类型标签。可通过第二个参数传入 {:key, value} 对列表来初始化属性。GetType 可获取对象的类型标签。

Let[obj, Object[Typ]];
Eq[GetType[obj], :Typ]; # => :true

Let[obj2, Object[Another, {
  { :k1, "v1" },
  { :k2, 2 }
}]];

不可变性与更新

KSL 中的对象严格不可变。SetDelete 不会修改原对象,而是返回带有变更的新对象。因此必须重新绑定变量来保留变更。

KSL 提供两种绑定方式:

  • Let:在当前作用域创建新的局部绑定,遮盖外层同名变量。作用域退出后外层变量不变。
  • Update:修改变量在声明作用域中的已有绑定,变更在当前块结束后仍然有效。
局部覆盖
Let[obj, Object[Typ]];
Block[
  Let[obj, Set[obj, :key1, "val1"]],
  Print[Has[obj, :key1]] # :true
];
Print[Has[obj, :key1]]; # :false
修改已有绑定
Block[
  Update[obj, Set[obj, :key1, "val1"]],
  Print[Has[obj, :key1]] # :true
];
Print[Has[obj, :key1]]; # :true
删除属性
Update[obj, Delete[obj, :key1]];

查看与访问

  • Keys[obj] 返回对象所有键的列表。键的顺序未定义。
  • Get[obj, :key] 获取指定键的值。
Let[obj, Object[:MyType, {
  { :name, "KSL" },
  { :ver, 1 }
}]];
Keys[obj];       # => { :name, :ver }
Get[obj, :name]; # => "KSL"

列表

列表是以花括号 { } 包围的有序序列,元素以逗号分隔。列表可包含任意类型,支持嵌套。

{ 1, "2", :a3 };
{ 1, 2, { 3, { 4 } } };

函数调用

语法

KSL 仅使用前缀表示法:函数名后跟一对方括号,参数以逗号分隔。无参函数也须书写空方括号。

FunctionName[arg1, arg2]

命名惯例

函数名是普通符号。内置函数名通常以大写字母开头。

匿名函数

匿名函数通过 Fun 构造,可立即调用或绑定到符号以定义命名函数。

# 直接调用
Fun[{ n }, Add[n, 1]][2]; # => 3

# 绑定为命名函数
Let[add1, Fun[{ n }, Add[n, 1]]];
add1[3]; # => 4

Block 表达式

Block 依次执行内部所有表达式,返回最后一个表达式的值。内部表达式以逗号分隔,每个表达式仍以分号结尾。

Block[
  Let[x, 10],
  Let[y, 20],
  Add[x, y]
]; # => 30

# 单表达式 Block
Block[
  Mul[3, 7]
]; # => 21

解构绑定

LetUpdate 支持通过列表一次性绑定多个符号。

Let[{ a, b, c }, { 1, 2, 3 }];
{ a, b, c }; => {1, 2, 3}

注释

KSL 支持三种注释形式:

行注释# 到行尾的内容为注释。

Let[x, 1]; # 行内注释
# 整行注释

块注释(**) 包围的内容为注释,可跨多行,支持嵌套。

Let[x, 1]; (* 块注释 *) Let[y, 2];

(*
  多行块注释
  (* 嵌套注释 *)
*)

文档注释(***) 包围的内容为文档注释,同样可跨多行。

(**
  文档注释内容,
  可跨越多行。
*)