|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
×
本帖最后由 ic_fisher 于 2025-12-23 19:04 编辑
一、访问操作符:统一接口背后的魔法
SKILL 提供了三种通用的访问语法,让不同数据类型的操作体验高度一致。
1. 箭头操作符 `->`:属性访问器
- 适用对象:离体属性列表、defstructs、关联表、用户自定义类型。
- 用法:`obj->prop`
- 本质:等价于函数 `getq(obj, prop)`。
- 限制:左右两侧都必须是符号(symbol)。
- designator = 'U235
- designator->x ; 等价于 getq('U235 'x)
复制代码
2. 波浪箭头 `~>`:批量处理神器
- 功能:对单个对象行为同 `->`;若作用于列表,则自动遍历每个元素并应用 `->`。
- 底层实现:`getSGq` 和 `setSGq`。
- 典型场景:批量修改一组图形对象的属性。
- rectList~>bBox = list(0:0 100:100) ; 所有矩形 bbox 同时更新
复制代码
3. 方括号 `[]`:索引与键值通吃
- 用途:
- 访问数组元素:`arr[0]`
- 访问关联列表(Assoc List)中的键值对:`alist["key"]`
- 访问关联表(Table):`table[key]`
注意:`[]` 是语法糖,不是函数调用,其行为由数据类型决定。
二、符号:SKILL 的变量本体
在 SKILL 中,符号远不止是变量名,它是一个拥有四个槽位的复合对象:
| 槽位 | 说明 | | Print Name | 符号的字符串名称,如 `"U235"` | | Value | 当前绑定的值(可为任意类型) | | Function Binding | 若该符号是函数名,则此处存函数体 | | Property List | 属性列表,用于存储元数据 |
关键操作
- 创建符号:
- `gensym('net)` → `net2`(自动生成唯一符号)
- `concat("cell", "1")` → 构造符号名(需配合 `intern` 或直接赋值)
- 获取打印名:`get_pname('U235)` → `"U235"`
- 赋值:
- 直接:`U235 = 100`
- 间接:`set('U235, 200)` 或 `set(location, 200)`(location 值为 `'U235`)
- 取值:
- 直接:`U235`
- 间接:`symeval('U235)` 或 `symeval(location)`
属性列表操作
- 设置:`setplist('U235, '(x 200 y 300))`
- 获取:`plist('U235)`
- 点操作符:`U235.x` → 等价于 `getqq(U235 x)`
- 箭头操作符:`designator->x` → 等价于 `getq(designator x)`
⚠️ 符号的属性列表是全局共享的!即使在 `let` 中修改,也会污染全局符号。务必使用唯一属性名,避免与其他脚本冲突。
三、离体属性列表:动态记录的轻量级方案
这是一种不依附于符号的属性列表,形式为 `(nil key1 val1 key2 val2 ...)`,可用 `->` 操作。
优势 vs 符号属性列表
- 无需创建符号(适合临时对象,如复数)
- 避免符号命名空间污染
- 更易作为参数传递
示例:复数运算
- complex1 = (nil imaginary 3 real 2)
- complex1->real ; → 2
复制代码
⚠️ 共享陷阱:赋值 `c1 = c2` 会让两者指向同一内存。修改 `c2->real` 会同步影响 `c1`!
正确做法:`c1 = copy(c2)`
属性操作函数
| 函数 | 说明 | 参数求值 | | `putprop` | 添加/更新属性 | 所有参数求值 | | `defprop` | 添加/更新属性 | 所有参数不求值 | | `get` | 获取属性值 | — | | `remprop` | 删除属性 | — |
- defprop(s 1+2 x) ; s.x → 1+2(表达式未求值!)
- putprop('s 1+2 'x) ; s.x → 3(表达式已求值)
复制代码
四、字符串:不只是文本
SKILL 字符串是字符数组,提供丰富的 C 风格操作。
核心函数速览
| 功能 | 函数 | 示例 | | 拼接 | `strcat` | `strcat("a", "b") → "ab"` | | 带分隔符拼接 | `buildString` | `buildString('("a" "b"), ".") → "a.b"` | | 子串提取 | `substring` | `substring("abc", 2, 2) → "bc"` | | 分割 | `parseString` | `parseString("a/b", "/") → ("a" "b")` | | 查找位置 | `nindex` | `nindex("abc", "b") → 2` | | 大小写转换 | `upperCase`, `lowerCase` | `upperCase("hello") → "HELLO"` |
字符表示的坑
- `getchar("1.2", 2)` 返回的是符号 \. ,而非字符串 "."
- 需用 `get_pname(getchar(...))` 才能得到字符字符串。
五、正则表达式:模式匹配利器
SKILL 支持两种正则引擎:传统 `rex*` 和更强大的 PCRE(Perl Compatible)。
基础用法
- rexMatchp("[0-9]+", "123") → t
- pcreMatchp("\\d{3}", "789") → t
复制代码 编译-执行模型
- `rexCompile(pattern)` / `pcreCompile(pattern)` → 编译模式
- `rexExecute(target)` / `pcreExecute(compiledObj, target)` → 执行匹配
注意:`rexCompile` 后,`rexExecute` 会复用上次编译结果。空模式 `""` 表示“沿用旧模式”。
特殊技巧
- `rexMagic(nil)`:关闭元字符(如 `*`, `.`)的特殊含义,进入字面匹配模式。
- `rexReplace`:替换匹配内容,支持 `\0` 引用整个匹配。
六、Defstructs:SKILL 的结构体
类似 C 的 `struct`,但更灵活。
定义与使用
- defstruct(card rank suit faceUp)
- aCard = make_card(?rank 'ace ?suit 'spades)
- aCard->rank ; → ace
- aCard->faceUp = t
复制代码
高级特性
- 动态添加字段:`aCard->color = 'red`(但效率低于静态字段)
- 批量操作:`cardList~>rank` → `(ace king ...)`
- 反射:
- `aCard->?` → `(rank suit faceUp)`(字段名列表)
- `aCard->??` → `(rank ace suit spades ...)`(字段名+值)
深拷贝警告
- `copy_card(aCard)`:浅拷贝(嵌套结构仍共享)
- `copyDefstructDeep(aCard)`:深拷贝,递归复制所有子结构
⚠️ 命名冲突:不要用结构体同名的变量,否则会遮蔽构造函数!
七、数组:高效随机访问
- 声明:`declare(arr[10])`
- 索引从 0 开始
- 运行时边界检查
- 赋值是引用传递:`b = a` → `a` 和 `b` 指向同一数组
多维模拟
- declare(matrix[3])
- for(i 0 2 matrix[i] = declare(row[3]))
- matrix[1][2] = 42
复制代码
八、关联表:哈希表的高效实现
基于哈希表,支持任意 SKILL 类型作 key(int, string, list, symbol 等)。
创建与操作
- myTable = makeTable("colors", "unknown")
- myTable[1] = "blue"
- myTable["two"] = 'red
- myTable[1] ; → "blue"
复制代码
遍历与查询
- foreach(key myTable println(list(key myTable[key])))
- forall(key myTable stringp(key)) ; 所有 key 是否为字符串?
复制代码
实用函数
| 函数 | 作用 | | `tableToList` | 转为关联列表(调试用) | | `writeTable` / `readTable` | 持久化到文件 | | `remove(key, table)` | 破坏性删除条目 | | `append(table, other)` | 合并其他数据结构 |
性能建议:少于10项用 Assoc List,更多用 Association Table。
九、关联列表与用户自定义类型
关联列表(Assoc List)
- 形式:`'(("A" 1) ("B" 2))`
- 查询:`assoc("B", alist)` → `("B" 2)`
- 更新:`rplaca(cdr(assoc("B", alist)), "two")`(破坏性修改)
用户自定义类型(User-Defined Types)
- 由应用程序(如数据库)导出,如 `dbOpen` 返回的对象。
- 行为由应用定义:`->`、`equal` 等操作会调用应用提供的方法。
- 对用户而言,使用体验类似 defstruct。
总结:
| 类别 | 操作 | 函数/语法 | | 符号 | 间接赋值 | `set(sym, val)` | | 间接取值 | `symeval(sym)` | | 属性设置 | `putprop`, `defprop` | | 属性获取 | `get`, `sym->prop`, `sym.prop` | | 离体属性列表 | 复制防共享 | `copy(list)` | | 字符串 | 字符提取 | `getchar(str, idx)` → 返回符号 | | 分割 | `parseString(str, delims)` | | 正则 | 匹配 | `rexMatchp`, `pcreMatchp` | | 替换 | `rexReplace` | | Defstruct | 深拷贝 | `copyDefstructDeep` | | 字段列表 | `obj->?`, `obj->??` | | 数组 | 声明 | `declare(arr[N])` | | 关联表 | 创建 | `makeTable(name, default)` | | 删除 | `remove(key, table)` | | 遍历 | `foreach(key table ...)` |
今天的内容是写出健壮、无副作用脚本的关键。整理不易,如果你对这些内容感兴趣,可以关注我的公众号“ic打工人摸鱼笔记”,感谢支持
|
|