在线咨询 切换到宽版
eetop公众号 创芯大讲堂 创芯人才网

 找回密码
 注册

手机号码,快捷登录

手机号码,快捷登录

搜全文
查看: 21|回复: 0

[原创] SKILL 数据结构全解析:从符号属性陷阱到关联表性能雷区

[复制链接]
发表于 4 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

×
本帖最后由 ic_fisher 于 2025-12-23 19:04 编辑

一、访问操作符:统一接口背后的魔法

SKILL 提供了三种通用的访问语法,让不同数据类型的操作体验高度一致。

1. 箭头操作符 `->`:属性访问器
- 适用对象:离体属性列表、defstructs、关联表、用户自定义类型。
- 用法:`obj->prop`
- 本质:等价于函数 `getq(obj, prop)`。
- 限制:左右两侧都必须是符号(symbol)。


   

        

                
  1. designator = 'U235
  2. designator->x  ; 等价于 getq('U235 'x)

  3.         

   

    复制代码

2. 波浪箭头 `~>`:批量处理神器
- 功能:对单个对象行为同 `->`;若作用于列表,则自动遍历每个元素并应用 `->`。
- 底层实现:`getSGq` 和 `setSGq`。
- 典型场景:批量修改一组图形对象的属性。


   

        

                
  1. rectList~>bBox = list(0:0 100:100)  ; 所有矩形 bbox 同时更新

  2.         

   

    复制代码

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 符号属性列表
- 无需创建符号(适合临时对象,如复数)
- 避免符号命名空间污染
- 更易作为参数传递

示例:复数运算


   

        

                
  1. complex1 = (nil imaginary 3 real 2)
  2. complex1->real  ; → 2
            

   

    复制代码


   
⚠️ 共享陷阱:赋值 `c1 = c2` 会让两者指向同一内存。修改 `c2->real` 会同步影响 `c1`!  
正确做法:`c1 = copy(c2)`


属性操作函数
函数说明参数求值
`putprop`添加/更新属性所有参数求值
`defprop`添加/更新属性所有参数不求值
`get`获取属性值
`remprop`删除属性


   

        

                
  1. defprop(s 1+2 x)  ; s.x → 1+2(表达式未求值!)
  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)。
基础用法


   

        

                
  1. rexMatchp("[0-9]+", "123") → t
  2. pcreMatchp("\\d{3}", "789") → t
            

   

    复制代码
编译-执行模型
- `rexCompile(pattern)` / `pcreCompile(pattern)` → 编译模式
- `rexExecute(target)` / `pcreExecute(compiledObj, target)` → 执行匹配


   
注意:`rexCompile` 后,`rexExecute` 会复用上次编译结果。空模式 `""` 表示“沿用旧模式”。


特殊技巧
- `rexMagic(nil)`:关闭元字符(如 `*`, `.`)的特殊含义,进入字面匹配模式。
- `rexReplace`:替换匹配内容,支持 `\0` 引用整个匹配。

六、Defstructs:SKILL 的结构体

类似 C 的 `struct`,但更灵活。

定义与使用


   

        

                
  1. defstruct(card rank suit faceUp)
  2. aCard = make_card(?rank 'ace ?suit 'spades)
  3. aCard->rank      ; → ace
  4. 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` 指向同一数组

多维模拟


   

        

                
  1. declare(matrix[3])
  2. for(i 0 2 matrix[i] = declare(row[3]))
  3. matrix[1][2] = 42
            

   

    复制代码

八、关联表:哈希表的高效实现

基于哈希表,支持任意 SKILL 类型作 key(int, string, list, symbol 等)。

创建与操作


   

        

                
  1. myTable = makeTable("colors", "unknown")
  2. myTable[1] = "blue"
  3. myTable["two"] = 'red
  4. myTable[1]  ; → "blue"

  5.         

   

    复制代码

遍历与查询


   

        

                
  1. foreach(key myTable println(list(key myTable[key])))
  2. forall(key myTable stringp(key))  ; 所有 key 是否为字符串?

  3.         

   

    复制代码

实用函数
函数作用
`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打工人摸鱼笔记”,感谢支持
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条

手机版| 小黑屋| 关于我们| 联系我们| 用户协议&隐私声明| 版权投诉通道| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 ) |网站地图

GMT+8, 2025-12-23 23:50 , Processed in 0.012684 second(s), 3 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表