开发工具数据可视化【免费下载链接】penroseCreate beautiful diagrams just by typing notation in plain text.项目地址https://gitcode.com/gh_mirrors/pe/penrose点击查看免费下载Collectors收集器是 Penrose Style 语言中一类特殊的样式块头header它把多个 Substance 变量聚合成一个集合从而让 Style 程序能够对一组匹配做求和、求平均、计数、横向分布等聚合操作。本文以官方参考文档为基础结合仓库中 Style 语法解析器 与 Style 编译器 的实现以及 examples 目录 中的真实 Style 程序系统讲解 Collectors 的语法、子句语义、分组机制、listof/numberof表达式并给出可直接复用的实战模式。读完本文你将能够在自己的 Penrose 图中用几行 Style 代码完成原本需要反复匹配才能实现的聚合布局。为什么需要 Collectors聚合的难题在 Selectors 参考文档 中我们看到Selector 负责把 Style 变量与 Substance 变量进行匹配产生多个相互独立的匹配match。每次匹配都是独立执行的这带来一个根本性的限制无法实现需要跨多个匹配进行聚合的功能。例如我们无法计算一个 Selector 所匹配到的所有Substance 变量的center字段之和——因为每个匹配各自为政彼此看不到对方。Collectors 正是为解决这类聚合需求而设计的它引入Substance 变量集合这一概念把若干 Substance 变量收集到一个命名集合中然后在样式块体内像操作普通变量一样操作这个集合例如求总和、算平均、取最大最小值、做均布约束等。Collectors 语法总览Collector 的完整语法如下collect COLLECT into INTO where WHERE with WITH foreach FOREACH { }其中where、with、foreach三个子句都是可选的如果某个子句内容为空则整个子句必须省略不能写空子句。四个占位符的含义分别是COLLECT一个对象声明object declaration它是被收集的对象。收集得到的对象通过INTO子句给出的名字在样式块体内访问。INTO为集合指定的名字。集合包含所有COLLECT匹配到的 Substance 对象在样式块体内INTO概念上代表一个Substance 对象列表。WHERE与标准forallSelector 中where子句含义相同是一组分号分隔的关系约束只有满足这些约束的匹配才会被收集。WITH分号分隔的对象声明列表。with中的对象不会被收集但可以在where子句中使用。FOREACH分号分隔的对象声明列表。foreach中的对象同样不被收集但它们用于把COLLECT收集到的对象分组。整个collect块会针对FOREACH子句的每一次不同匹配运行一次foreach列表可以包含多个声明。从语法解析器的实现可以印证这一结构在 Style.ne 中collector产生式枚举了collect、into、where、with、foreach五个部分的所有合法排列组合共 20 种并将结果构造成tag: Collector的 AST 节点字段分别为repeatable、head、into、where、with、foreach。也就是说where/with/foreach三个子句顺序可以任意调换只要collect ... into ...在前即可。一个完整的运行示例按集合分组收集元素文档给出了一个最典型的场景Substance 程序定义了两个集合s1、s2分别包含元素e1, e2与e3, e4, e5Set s1 Element e1, e2 In(e1, s1) In(e2, s1) Set s2 Element e3, e4, e5 In(e3, s2) In(e4, s2) In(e5, s2)对应的 Collector 写法collect Element e into es where In(e, s) foreach Set s { ... }在上述 Substance 程序下这个 Collector 会运行两次第一次es - [e1, e2]s - s1第二次es - [e3, e4, e5]s - s2可以看到全体元素[e1, e2, e3, e4, e5]被按照它们所属的Set对象拆分成了两个分组。这正是foreach子句的作用COLLECT即e负责收集FOREACH即s负责定义分组的键。这个分组过程在编译器中有非常清晰的实现。在 Style.ts 的 collectSubsts 函数 中编译器先把collect、with、foreach三部分的声明合并见 getDecls再对 Substance 程序做整体匹配得到一组原始替换substitution对每一条替换取出COLLECT对应的对象以及foreach中每个分组变量对应的对象用分组变量对象集合的唯一名字串作为桶bucket的键subObjectToUniqueName(...).join( )把COLLECT对象放入对应桶中最终每个桶产出一个CollectionSubst其collContent就是该分组下的对象列表。因此 collect块对每个 distinct 的 foreach 匹配运行一次 并非文档层面的空泛承诺而是编译器用 Map 分桶实现的确定行为。Collector 块体内的能力与限制在 Collector 的块体内几乎可以做 Selector Block 中能做的一切事情唯一的例外是只能访问INTO与FOREACH中声明的样式变量。也就是说where与with子句中的对象只参与匹配条件的判定不会进入块体的可见作用域。这一点与 Selector 的差异非常关键Selector 中forall、with声明的变量在块体内都可以访问详见 Selectors 文档而 Collector 刻意收窄了作用域以换取集合这一抽象。实践中这意味着如果你需要在块体内使用某个辅助对象请把它放进foreach并接受它会带来分组的副作用或者依赖集合访问表达式去提取集合内对象的字段。Repeatable 与 Non-Repeatable 匹配Collector 运行的是与 Selector完全相同的底层匹配算法参见 Selectors 文档的匹配算法章节。因此默认情况下它同样不允许两个样式变量映射到同一个 Substance 变量。要覆盖这一默认行为与 Selector 中一样参见 Repeatable vs Non-Repeatable 章节可以在collect关键字后加上repeatable关键字collect repeatable Set s into ss where Subset(s, a) with Set a在 Style.ne 的语法中(repeatable __):?是一个可选前缀在 Style.ts 的 getSubsts 函数 中repeatable为true时直接返回去重后的全部替换否则还会经过uniqueKeysAndVals过滤掉同一组键值的重复映射。此外匹配算法本身带有去重机制如果两个映射对应相同的 Substance 对象集合与相同的关系集合则只会触发一次参见 Selectors 的匹配去重章节。集合访问表达式listof在 Collector 块体内INTO名字概念上是一个 Substance 对象列表。要访问集合中每个元素的字段需要使用Collection Access集合访问表达式listof FIELD NAME from COLLECTION NAME该表达式取出集合COLLECTION NAME中每个 Substance 变量的FIELD NAME字段并将这些字段编译成一个合适的列表。例如假设elements是INTO名字概念上代表对象列表[e1, e2, ..., en]那么listof field from elements就会产出列表[e1.field, e2.field, ..., en.field]。由于技术约束并非任意字段类型都能被收集成列表——例如 Penrose 不支持颜色列表如果e1.field是颜色就无法放入列表中。文档明确给出了允许的类型映射表field的类型收集成的类型FloatV数值VectorV向量VectorV向量MatrixV矩阵ListV列表LListV列表的列表TupV2 元组PtListV二维点列表某种 shape图形ShapeListV图形列表例如若e1.field、e2.field等都是数值那么listof field from elements会得到一个包含全部取值的向量可以直接把它塞进average、sum等聚合函数。从语法层面看Style.ne 的 sty_var_expr 产生式 定义了listof _ identifier _ from _ path形式生成tag: CollectionAccess节点。编译期求值则发生在 Style.ts 的 CollectionAccess 分支编译器遍历集合中每个 Substance 对象构造唯一名.字段的路径并在符号表中取值最后调用collectIntoVal根据元素的实际类型组装成向量、矩阵、点列表或图形列表对应 Style.ts 附近的类型分支逻辑使用llistV、ptListV、shapeListV等构造器。集合计数count 与 numberof统计集合中元素的数量是一类常见需求尤其是在计算两个对象之间的间距时。利用前面提到的listof表达式可以这样写collect Object o into os { total count(listof someprop from os) }count函数接收一个数值向量并返回向量中元素的个数。这种方法虽然可行但存在两个隐含假设每个Object o都必须具有someprop字段且该字段必须是数值。为了简化这种写法Collector 专门提供了numberof表达式numberof COLLECTION NAME它直接返回COLLECTION NAME中的元素个数不依赖任何具体字段。编译器对它的实现非常直接——evalNumberOf 函数 检查路径解析结果是否为集合若是则返回substanceObjects.length作为浮点数值。顺带一提与numberof并列语法解析器还支持nameof表达式Style.ne它返回某个 Substance 对象的名字字符串evalNameOf 实现。虽然官方 Collector 文档没有展开介绍它但它在仓库示例中常用于拼接错误信息文本例如 table.style 中的Error: philosopher (nameof p) uses fork (nameof f) not adjacent to them。仓库实战案例剖析文档之外仓库的 examples 目录 提供了大量 Collector 的真实用法是理解这些特性的最佳补充教材。数组模型numberof 统计元素个数在 arrayModels.style 中用numberof把每个数组的元素个数写入scalarcollect Element e into es where inArray(e, a, z) foreach Array a { scalar a.numElements numberof es }这里foreach Array a让每个数组各跑一次 Collectornumberof es就是该数组的元素数量。同文件稍后arrayModels.style还展示了更复杂的聚合用average(listof u from es)、sum(listof width from es)、maxList(listof v from es) - minList(listof v from es)计算数组外框的中心与尺寸把listof的字段提取 聚合函数组合运用得淋漓尽致。冰柱图文件系统sum 均布约束在 icicle-plot-file-system.style 中listof直接喂给约束与布局函数collect Entry e into es where entries(d, e) foreach Dir d { override d.width sum(listof width from es) ensure distributeHorizontally(listof icon from es) }目录的宽度等于其下所有条目的宽度之和sum(listof width from es)条目的图标则通过distributeHorizontally(listof icon from es)做水平均布——这里listof icon收集的是图形shape对应类型映射表中的ShapeListV。集合土豆分组布局与全局平衡set-potato.style 展示了两个思路一是按集合分组让集合内元素垂直均布并计算集合高度collect Element e into es where SetHasElement(s, e) foreach Set s { points listof icon from es ensure distributeVertically(points, Global.pointSpread) override s.icon.ry (numberof es - 0.5) * Global.pointSpread Global.setHeightPadding avgCenter averagePoint(listof center from es) ensure equal(avgCenter[1], s.center[1]) }二是不带 foreach的全局聚合——把所有集合的中心收集起来求平均再约束整个画布的平衡collect Set s into ss { avgCenter averagePoint(listof center from ss) ensure equal(norm(avgCenter - Global.box.center), 0) }注意第二个 Collector 没有where、with、foreach子句这正是文档所说空子句必须省略的合法形态此时收集范围就是 Substance 程序中所有Set对象。消息传递时间线平均坐标对齐在 timeline-message.style 中用average(listof yCoord from ms)约束消息整体的垂直位置与listof xCoord、foreach Time t配合完成多参与方时间线的排布。这些案例共同说明了 Collector 的典型应用模式先按某个关键对象分组再对组内字段做统计或均布。常见错误与注意事项使用 Collector 时有几类典型的编译期错误值得注意仓库的 Error.ts 给出了它们的精确诊断信息对集合使用点操作符集合不是一个普通对象不能直接写es.somefield。错误信息会明确提示...是一个集合不能使用点操作符访问。要访问集合中每个成员的字段请使用集合访问表达式listof field from 集合名。在需要字面值的位置使用集合当某个上下文要求非集合的数值/字面量而路径解析结果是一个集合时编译器会报错并建议改用listof ... from ...或numberof 集合名。listof的类型约束如果字段类型不在类型映射表中例如颜色无法编译成列表设计时应提前规划好被收集字段的类型。作用域限制Collector 块体内只能访问INTO与FOREACH中的样式变量where/with中的变量不可见误用会得到未定义变量错误。总结Collectors 是 Penrose Style 语言中实现跨匹配聚合的核心机制其设计可以概括为四步collect声明被收集对象into给它一个集合名where/with约束参与匹配的对象foreach决定分组方式随后在块体内用listof ... from ...提取字段、用numberof计数配合sum、average、minList、maxList、distributeHorizontally/distributeVertically等函数完成统计与布局。本文所引用的语法与实现细节均来自仓库源码Style 语法文件、Style 编译器、Style 编译测试其中 Style.test.ts 直接验证了numberof ts与count(listof value from ts)的求值结果以及 examples 中的多个真实 Style 程序。若想进一步掌握 Selector 与 Collector 共用的匹配算法建议继续阅读 Selectors 参考文档 与 Substance 语言总览。赞分享开发工具数据可视化【免费下载链接】penroseCreate beautiful diagrams just by typing notation in plain text.项目地址https://gitcode.com/gh_mirrors/pe/penrose点击查看免费下载相关推荐Encore Service Catalog由 Application Model 自动生成、永不过期的 API 文档体系Encore Service Catalog由 Application Model 自动生成、永不过期的 API 文档体系 导读 本文围绕 Encore 平台开发工具数据可视化Penrose 实战指南用 Domain–Substance–Style 三语言纯文本绘制集合论欧拉图Penrose 实战指南用 Domain–Substance–Style 三语言纯文本绘制集合论欧拉图 Penrose 是一个只靠纯文本符号就能生成高质量图开发工具数据可视化GitHub Copilot SDK会话持久化跨重启恢复会话的完整方案GitHub Copilot SDK会话持久化跨重启恢复会话的完整方案 GitHub Copilot SDK是一款多平台软件开发工具包专为将GitHub C开发工具数据可视化上一篇7种截图模式Ksnip跨平台截图工具终极指南下一篇10分钟掌握node-fetch断点续传基于Range请求头的终极文件下载指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
