gt语言和gtl区别是什么?
GT语言
GT语言并不是一个单一明确的编程语言,而是有多种可能的含义:
-
GNU Data Language (GDL)
全称:GNU Data Language
性质:开源的数据处理和可视化工具
兼容性:与IDL(Interactive Data Language)和PV-WAVE兼容
应用领域:科研、天文、地球科学、医学成像等
特点:动态类型、数组导向、支持向量化操作、面向对象编程能力
功能:提供丰富的库函数,涵盖数值计算、图形化展示、信号与图像处理等
-
通用描述语言 (Generic Description Language)
性质:形式语言,用于描述语言的语法规则
应用:语音识别、自然语言处理、编译器开发
功能:定义语言中允许的词语序列、短语结构和句子结构
-
图形化编程技术 (Graphic Technology)
性质:通过图形界面进行编程的方法
特点:使用拖拽图形块构建程序,而非传统代码编写
应用:教育领域,如Scratch等工具
GDL语言
GDL语言主要指的是:
几何描述语言 (Geometric Description Language)
全称:Geometric Description Language
性质:参数化程序设计语言,专门为建筑师和设计师开发
应用:创建和控制ArchiCAD中的对象和元素
特点: 支持参数化设计,可创建可配置的对象 允许创建自定义建筑元素,如家具、窗户、门等 对象可以是静态或动态的,具有参数化特性
支持精确控制对象的外观和行为
主要区别对比
| 特性 | GT语言 | GDL语言 |
|---|---|---|
| 应用领域 | 数据处理、科学计算、图形化编程 | 建筑设计、参数化建模 |
| 主要用途 | 数据分析、可视化、教育编程 | 建筑元素创建、参数化设计 |
| 语言类型 | 数据处理语言、图形化编程语言 | 几何描述语言 |
| 目标用户 | 数据科学家、研究人员、教育工作者 | 建筑师、设计师、BIM工程师 |
| 技术特点 | 数组导向、向量化操作、兼容IDL | 参数化、几何建模、与CAD集成 |
FF时间管理
在特定数据库或数据模型中,管理记录生效时间和学习(或操作)时间的复杂逻辑方案。其核心思想是通过维护一系列时间区间来跟踪一条记录(比如一个客户)在其生命周期内不同时间点的状态。
| 管理类型 | 核心场景描述 | 关键步骤 | 核心操作概括 |
|---|---|---|---|
| 当天时间管理 | 处理生效日期(Date_effect) 与操作/学习日期(Date_learn) 为同一天的记录。 | 1. 查询:找出所有当前生效且需要更新的记录。 2. 新增:插入一条代表当天新状态的全新记录。 3. 修改:更新旧记录的结束时间,使其指向新记录,以关闭旧区间。 | “开新关旧” :为当天开启一个新的有效状态区间,并关闭旧的区间。 |
| 历史时间管理 | 处理生效日期(Date_effect) 早于操作/学习日期(Date_learn) 的记录(即回溯修改历史)。 | 1. 查询受影响范围:确定新记录的插入会影响哪些已有的历史记录区间。 2. 新增记录(1+N) : - 首行(1) :为新生效日开辟一个独立的时间段。 - 其余(N) :对受影响区间进行分割和调整,生成新的时间段。 3. 修改连接:更新受影响记录的指针,重新链接时间线,确保连续性。 | “区间分割与重组” :像处理时间线段一样,插入新的时间点,并将原有区 |
| 关键字段 | 疑似含义(基于上下文推测) | 在时间区间中的作用 |
|---|---|---|
o__edn | 记录状态的生效开始日期(Effect Date From) | 时间区间的起点 |
o__edx | 记录状态的生效结束日期(Effect Date To) | 时间区间的终点 |
o__kdn | 该条记录被创建或知晓的开始日期(Known Date From) | 标识此记录版本何时开始有效 |
o__kdx | 该条记录被创建或知晓的结束日期(Known Date To) | 标识此记录版本何时不再有效 |
o__kdxa | 可能是一个指针或链接标识,指向下一条相关记录 | 用于维护记录在时间上的连续性 |
999999 | 代表“无限未来”或“至今有效”的占位符 | 表示一个尚未结束的开放区间 |
每条记录都代表一个客户在某个时间区间内的一个有效状态片段。通过 o__edn和 o__edx定义这个状态在业务上有效的时间范围,同时通过 o__kdn和 o__kdx跟踪这个记录片段本身在系统中的生命周期。o__kdxa和 999999这样的标志则用于将这些片段在时间线上正确地链接起来。
假设我们要记录客户“会员等级”的变化:
第一天(81116) :客户注册,成为普通会员。系统会新增一条记录:(o__edn=81116, o__kdn=81116, o__edx=999999, o__kdx=999999)。这表示“普通会员”状态从81116生效,并且目前一直有效。
第二天(81117) :客户升级为VIP会员。此时就会触发“当天时间管理”逻辑: 查询:找到当前有效的记录(即昨天那条记录)。 新增:插入一条新记录代表VIP状态:(o__edn=81117, o__kdn=81117, o__edx=999999, o__kdx=999999)。 修改:将昨天那条普通会员记录的结束日期标记为今天之前,即修改为:(o__edn=81116, o__kdn=81116, o__edx=81116, o__kdx=81117)。这样,普通会员状态的有效期就被精确地界定在81116这一天。
这个方案本质上是在实现一个支持时间旅行查询的临时数据库。它的强大之处在于可以回答“在历史上的任意时间点,某个客户的有效状态是什么? ”以及“这个状态是在何时被系统确认的? ”这类问题。
GT日期及历史管理
| 功能领域 | 核心概念/操作 | 简要说明 |
|---|---|---|
| 日期与时间 | 获取系统日期 | 使用 DATE、TIME等保留变量直接从操作系统获取当前日期和时间。 |
| 自定义日期变量 | 定义变量存储特定日期,用于参数化控制(如模型版本日期)。 | |
| 日期格式转换 | 使用 STR等函数将日期数值格式化为特定格式的字符串。 | |
| 历史管理 | 参数化驱动 | 通过修改关键参数(如日期、尺寸)的值,回溯或生成不同历史状态的设计。 |
| 脚本逻辑控制 | 利用 IF...THEN、FOR等控制结构,根据不同日期条件执行不同几何生成操作。 | |
| 版本注释 | 在脚本中添加注释,记录重要历史更改的原因、日期和作者。 |
💡 日期处理基础
在GDL中处理日期,通常涉及以下几个方面:
系统日期获取:GDL可以通过一些保留变量或内置函数来获取当前的系统日期和时间。这常用于在模型中自动标记创建日期或时间戳。
自定义日期变量:你可以定义变量来存储特定的日期。例如,可以创建一个变量 Project_StartDate并将其设置为一个特定的日期值,用于控制与项目开始日期相关的参数或显示。
日期格式转换:使用如 STR等函数,可以将日期数值转换为特定格式的字符串,以便于在模型的标签或注释中显示。
🔄 历史管理思路
GDL本身不像数据库那样自动记录每一步操作历史,但其参数化和脚本化的特性为管理设计历史提供了强大灵活性:
参数化驱动历史回溯:这是最核心的方法。通过修改控制模型的关键参数(例如一个代表“设计阶段”的日期参数),可以立即将模型回溯到该日期对应的状态,或者生成不同历史版本的设计方案。
脚本逻辑控制版本:在GDL脚本中,可以使用 IF...THEN、SELECT、FOR等控制结构,根据不同的日期或版本参数值,执行不同的几何生成逻辑、加载不同的材质或显示不同的文本注释。这使得一个GDL对象可以包含多个历史版本的设计变体。
注释的重要性:在GDL脚本中,使用 !符号添加详细的注释,说明某段代码或某个参数在特定日期被修改的原因,这对于后期理解和维护设计意图至关重要。
变量和标识符
gt为弱引用,不需要显式的声明类型
// Java中的变量声明
int length = 100;
double height = 250.5;
String material = "混凝土";
// GDL中的变量声明(类似)
length = 100
height = 250.5
material = "混凝土"
| GDL变量类型 | Java中的近似概念 | 说明 |
|---|---|---|
| 本地变量 | 方法内部的局部变量 | 这是我们当前的重点,作用域和生命周期类似 |
| 匿名变量 | 匿名对象或Lambda表达式中的临时变量 | 用于一些不需要命名的临时场景 |
| 保留变量 | Java的关键字(如 int, class)或系统常量 | GDL语言自身定义的特殊变量,有固定含义 |
| 全局变量 | 类的静态变量(public static)或常量 | 在整个GDL脚本/项目中都可以访问 |
本地变量
| 特性 | Java 局部变量 | GDL 本地变量 |
|---|---|---|
| 命名规则 | 以小写字母开头(驼峰命名) | 以大写字母或下划线 _开头 |
| 例子 | String name;``int age; | Name, _Age, BatchCtrl |
| 作用域 | 声明它的代码块 {}内 | 初始化它的程序段(如方法、循环)内 |
| 赋值 | 可多次赋值:age = 18; age = 19; | 只能赋值一次,之后不能再改变其值 |
GDL本地变量的两大独特特性
特性一:Free变量(未赋值的变量)
但在GDL中,这是一个完全合法的概念,叫做 Free变量:
它有一个随机的初始值(如 38171)
你可以用 is free方法来判断它是否为Free状态
这为参数化设计提供了灵活性
可以只赋值但是不使用,那样他的值就是初始化赋予的
特性二:变量只能赋值一次
这是最关键的差异。在Java中,变量可以反复修改:
// Java - 完全正确
int count = 10; // 第一次赋值
count = 20; // ✅ 重新赋值
count = 30; // ✅ 再次重新赋值
而在GDL中,一个初始化过的本地变量永远不能被重新赋值:
! GDL示例
Name = "墙体" ! ✅ 第一次赋值(初始化)
Name = "窗户" ! ❌ 错误!不能重新赋值
GDL的正确做法是:只能比较,不能重新赋值
Name = "墙体"
IF Name == "墙体" THEN ! ✅ 可以比较
! 执行一些操作
ENDIF
举例
! ========== GDL 代码示例 ==========
! 本地变量声明和初始化
MaterialName = "玻璃幕墙" ! ✅ 以大写字母开头
_Thickness = 150 ! ✅ 以下划线开头
! 检查是否为Free变量(未赋值的变量)
IF is free(Height) THEN ! ✅ 判断Height是否未赋值
Height = 3000 ! ✅ 如果是Free状态,可以第一次赋值
ENDIF
! 尝试重新赋值 - 这是错误的!
! MaterialName = "石材" ! ❌ 错误!已初始化的变量不能重新赋值
! 正确的使用方式:比较和逻辑判断
IF MaterialName == "玻璃幕墙" THEN
Transparency = 80
ENDIF
! 在循环中使用本地变量
FOR i = 1 TO 5
ElementName = "构件_" + str(i, 1, 0) ! ✅ 循环内的本地变量
! 这里可以绘制几何体...
NEXT i
! 超出作用域后,ElementName就不再可用了
全局变量
| 特性 | Java 静态变量/类变量 | GDL 全局变量 |
|---|---|---|
| 命名规则 | 通常大写字母+下划线(如 MAX_COUNT) | 首字符为字母,且必须包含一个下划线 _ |
| 例子 | public static final String PROJECT_NAME; | ProjectName, Material_Type |
| 作用域 | 整个类(甚至整个应用)可见 | 整个GDL脚本文件都可见和可访问 |
| 生命周期 | 随类加载而创建,程序结束才销毁 | 脚本执行期间一直存在 |
这是GDL全局变量最特殊的地方,PPT中没有明确写出但极其重要:
本地变量:以大写字母或_开头(如 Name, _Height)
全局变量:必须包含下划线,且不能只是以下划线开头(如 Project_Name, Global_Material)
! ========== 正确示例 ==========
! 本地变量
ElementName = "窗户" ! ✅ 首字母大写
_Thickness = 200 ! ✅ 以下划线开头(但这是本地变量!)
! 全局变量
Project_Name = "办公楼" ! ✅ 包含下划线(关键特征!)
Global_Material = "玻璃" ! ✅ 包含下划线
Window_Type_01 = "推拉窗" ! ✅ 包含下划线
! ========== 错误示例 ==========
! GlobalMaterial = "钢材" ! ❌ 没有下划线,会被当作本地变量
! _ProjectName = "酒店" ! ❌ 仅以下划线开头,是本地变量而非全局
作用域:无处不在
全局变量一旦定义,在脚本的任何地方(不同方法、循环、条件语句内)都可以直接使用。
与本地变量的关系:遮蔽效应
这和Java完全一样:当全局变量和本地变量同名时,本地变量会"遮蔽"全局变量。
匿名变量
匿名变量是没有显式名称的临时变量,通常在使用后立即丢弃,不保留引用。
! 场景1:直接使用表达式结果,不赋给变量
! 而不是先赋值给变量再使用
ADDX 100 + 50 ! ✅ 直接使用表达式150,匿名临时值
! 对比:先赋给变量(需要命名)
! offset = 100 + 50
! ADDX offset
! 场景2:函数返回值的直接使用
ROTATE ATN(1) * 2 ! ✅ ATN()返回的反正切值就是匿名变量
! 场景3:循环中的临时索引
FOR i = 1 TO 5 ! ✅ i就是匿名变量,循环结束就丢弃
BLOCK 100, 100, 100
ADDX 150
NEXT i
保留变量
// Java中的关键字和系统常量就是类似概念
System.out // 预定义的输出流
Math.PI // 预定义的数学常量
Integer.MAX_VALUE // 预定义的最大整数值
| 类别 | 示例保留变量 | 功能描述 |
|---|---|---|
| 几何属性 | GLOB_SCALE, CURRENT_VIEW | 控制全局缩放、当前视图等 |
| 材料属性 | MATERIAL, PEN | 定义对象材料、画笔属性 |
| 版本控制 | GDL_VERSION, LIBRARY_VERSION | GDL和库版本信息 |
| 系统信息 | SYSTEM_TIME, SYSTEM_DATE | 获取系统时间日期 |
! 使用保留变量获取系统信息
versionInfo = "GDL版本: " + STR(GDL_VERSION, 1, 0)
currentMaterial = MATERIAL ! 获取当前材料设置
! 基于保留变量的条件判断
IF GLOB_SCALE > 100 THEN
! 在大比例尺下显示细节
DETAIL_LEVEL = 2
ELSE
! 在小比例尺下简化显示
DETAIL_LEVEL = 1
ENDIF
! 使用保留变量设置属性
PEN = 5 ! 设置画笔为5号颜色
LINE_TYPE = 2 ! 设置线型为2号
标识符
| 标识符类型 | 起始字符/格式 | 示例 | 常见用途 | 关键特性 |
|---|---|---|---|---|
| 一般标识符 | 小写字母或单引号内的内容 | blue, false, 'hello world' | 常量定义,表示固定的数值或字符串 | 是标识符(Ident),不是变量,不能赋值 |
| 特殊标识符 | 可能以特定符号(如*)开头或大写字母 | *Value, *Template, CONSTRUCT | 预定义关键字,模板名,构造名,指令 | 通常有特殊含义,用于定义结构或指令 |
| 特性 | 当前语言 (基于描述) | Java |
|---|---|---|
| 一般标识符 | 代表常量值,不可变 | 变量名、方法名、类名等,变量可被重新赋值 |
| 核心用途 | 侧重于数据描述和配置 | 侧重于逻辑实现和计算 |
特殊标识符
| 特殊标识符 | 全称/含义 | 功能描述 | 示例解析 (Obj = $header:cp_contract$100sRdss) |
|---|---|---|---|
CID | 类唯一标识(Class Identifier) | 提取对象所属的类别或类型。 | $header:cp_contract(对象属于“cp_contract”类) |
OID | 对象唯一标识(Object Identifier) | 提取对象的全局唯一标识符。 | $header:cp_contract$100sRdss(对象的完整唯一ID) |
Int(可能指 usr_oid) | 实例标识(Instance Identifier) | 提取对象唯一标识符中特定于实例的部分(通常是OID的最后一段)。 | 100sRdss(剥离类别前缀后,该对象实例的唯一编号) |
列表、特殊运算符及属性列表 ➔ 集合与操作符
GDL有很多几何相关的运算符,如向量运算、坐标变换等
// Java中的数组/列表
double[] dimensions = {100, 200, 300};
String[] materials = {"玻璃", "钢材", "木材"};
// GDL中的列表(更简单直观)
dimensions = 100, 200, 300
materials = "玻璃", "钢材", "木材"
| 概念 | 语法形式/关键字 | 核心规则与示例 | 特别注意 |
|---|---|---|---|
| 列表 | [元素1, 元素2, ..., 元素n] | - 空列表: []或 nil - 元素类型可不同: [1, "hello", a, [嵌套列表]] - 元素默认不计算: [2 * 3]存储的是表达式2 * 3本身,而非结果6。 | 列表是数据结构,用于存储多个元素,其中的算符在默认情况下不做计算。 |
| 评估 | ?(表达式) | - 作用: 强制立即计算括号内的表达式,并使用其结果。 - 示例1: L = [?(2 * 3), ?("ab" ++ "cd")]结果为 L = [6, "abcd"]。 - 示例2: V = ?(2 * 3)结果为 V = 6。 | ?()是一个计算指令,它打破了列表默认的“不计算” |
管道操作符
| 表达式 | 含义 | 在你的示例 L = [a, b, c, d, e]中的匹配结果 |
|---|---|---|
| `[H | T]` | 将列表分为第一个元素(头)和剩余所有元素(尾) |
| `[A, B, C | _]` | 匹配列表的前三个元素,并忽略剩余部分(_) |
| `[H | T](当L = [a]`) | 单元素列表:头是该元素,尾是空列表 [] |
| `[H | T](当L = nil/[]`) | 匹配失败,因为空列表没有头部 |
举例
% 假设我们有一个列表:L = [a, b, c, d, e]
% 使用 [H | T] 模式去匹配它:
[H | T] = [a, b, c, d, e].
% 匹配后,变量的值是:
% H = a (列表的第一个元素)
% T = [b, c, d, e] (第一个元素之后的所有元素组成的**新列表**)
% 甚至可以继续解构尾部:
[H2 | T2] = T.
% 现在 H2 = b, T2 = [c, d, e]
% 也可以匹配更多元素:
[A, B, C | Rest] = [a, b, c, d, e].
% A = a, B = b, C = c, Rest = [d, e]
| 语言/环境 | 类似功能 | 说明 |
|---|---|---|
| Erlang, Elixir, Prolog | `[H | T]` |
| Python | list[0], list[1:] | 通过索引获取第一个元素和切片获取剩余部分,但这不是模式匹配。 |
| Java | list.get(0), list.subList(1, list.size()) | 通过方法调用实现类似功能,概念和语法完全不同。 |
运算符号
| 运算符 | 名称 | 核心作用 | 关键特性 | 形象比喻 |
|---|---|---|---|---|
= | 赋值运算 | 将右边表达式的计算结果赋予左边的变量。 | 1. 不可交换:方向固定(左变量,右值) 2. 强制计算右边:先算后赋 3. 一次性:变量一旦被赋值,不能重新赋值 | ****“贴标签”**:给一个计算好的结果(比如数字5)贴上一个名字标签(如X)。 |
<=> | 合一运算 | 声明两个结构在形式上是等价的,而不关心其内部计算。 | 1. 可交换:两边位置可互换 2. 不强制计算:只进行结构比对,不计算值 3. 用于模式匹配 | “拼图匹配” :检查两块拼图的轮廓是否能严丝合缝地对上,不关心拼图上的图案细节。 |
== | 比较运算 | 判断左右两边表达式的最终值是否相等。 | 1. 可交换:两边位置可互换 2. 两边均强制计算:先分别算出结果再比较 | “品鉴定” :比较两颗宝石的最终成色、净度、重量是否完全相同。 |
🧠 如何理解“强制计算”
这里的“强制计算”是理解差异的关键:
在 =和 ==中,如果表达式是 2 + 3,会先算出结果 5再进行赋值或比较。
而在 <=>中,它可能直接将 2 + 3这个表达式本身与另一个结构进行比对,而不会先去计算它等于5。
属性列表
| 特性/操作 | 说明 | 示例 |
|---|---|---|
| 基本形式 | 列表形式,每个元素都是 标识符 = 值的键值对。 | [name = "Jay", age = 16] |
| 结构灵活性 | 值可以是基本数据类型(字符串、数字)、列表,甚至是复杂的对象标识符。 | [$header:cp_contract$1 = ["27860100", "27540100"]] |
| 易读性 | key = value的直观形式,让人一眼就能理解数据的含义。 | [weight = 60, height = 170] |
| 快速获取 | 通过唯一的标识符(key)可以直接访问对应的值。 | 通过 name键快速获取 "Jay" |
属性列表的本质是一个列表,但其每个元素都是一个清晰的键值对(Key-Value Pair)。这种结构非常适合用于存储配置信息、对象属性或任何需要通过名称快速访问的数据
键(Key/Ident) :通常是一个标识符,用于唯一确定该属性,如示例中的 name, age, 以及更复杂的 $header:cp_contract$1。
值(Value) :可以是任意类型的数据,包括字符串、数字、列表,甚至是另一个属性列表,这构成了其结构灵活的基石
路径、调用和多次调用 ➔ 方法与函数调用
// Java中的方法定义和调用
public void drawWall(double width, double height) {
// 绘制逻辑
}
// 调用
drawWall(100, 200);
对象属性操作
| 核心概念 | 语法/结构 | 功能描述 | 示例 |
|---|---|---|---|
| 简单调用 (get) | get:Object.Property | 获取对象某个属性的值。 | CvrgCode = get:Cvrg.risk_code |
| 简单赋值 (set) | set:Object.Property = Value | 设置对象某个属性的值。 | set:Cvrg.status_1 = "in_force" |
| 路径 (Path) | $Class$OID->Link1->Link2.Property | 通过一系列链接(link)导航到目标实例并访问其属性。 | Name = get:$header:cp_contract$1234->l_owner_1.name |
1. 简单调用 (get)
get操作用于从对象中读取特定属性的值。这就像使用钥匙打开一个特定的抽屉查看里面的内容。
工作原理:get:后面跟随的是对象的标识符和属性的名称,两者用点号 .分隔。
示例解析:CvrgCode = get:Cvrg.risk_code Cvrg是目标对象的标识符。 risk_code是该对象中你需要读取的属性名。
执行后,属性 risk_code的值会被取出并赋给变量 CvrgCode。
2. 简单赋值 (set)
set操作用于写入或修改对象的属性值。这就像你向那个特定的抽屉里放入或更换一件物品。
工作原理:set:后面同样跟随对象标识符和属性名,然后使用等号 =为其赋予一个新的值。
示例解析:set:Cvrg.status_1 = "in_force"
这行代码将 Cvrg对象的 status_1属性值设置(或更新)为字符串 "in_force"。
3. 路径 (Path)
路径是GT语言中一个非常强大的特性,它允许你通过对象之间预定义的关联(链接)来“穿梭”于复杂的对象网络中,最终访问到目标对象的属性。这就像根据一个具体的地址(如:A栋楼->B单元->1001室)找到最终的目的地。
路径结构:$Class$OID->Link1->Link2.Property $Class$OID: 这是路径的起点,指定了你要从哪个具体的对象实例开始。例如 $header:cp_contract$1234。 ->Link1->Link2: 这部分是路径,由一个个“链接”组成。每个链接都代表对象间的一种关联关系(例如,一个合同对象可能有一个名为 l_owner_1的链接,指向其对应的所有者对象)。 .Property: 这是路径的终点,指定了你最终要访问的目标属性(例如,所有者对象的 name属性)。
示例解析:
获取值:Name = get:$header:cp_contract$1234->l_owner_1.name 含义:从ID为 1234的 cp_contract类对象开始,沿着名为 l_owner_1的链接找到它所指向的对象,然后获取那个对象的 name属性值,最后将其赋给变量 Name。
设置值:set:$cvrg:cp_cvrg_death$2346->l_cntrct->l_owner_1.name = "Jay" 含义:这是一个更长的路径。从ID为 2346的保障对象开始,先通过 l_cntrct链接找到其关联的合同,再通过合同的 l_owner_1链接找到最终的所有者对象,并将其 name属性设置为 "Jay"。
通用调用
| 组件 | 可选值 / 形式 | 含义与功能 |
|---|---|---|
| Msg_type (消息类型) | get:, set:, getm:, m:, t:, :, x | 定义操作的核心意图,如获取、设置、调用方法等。 |
| Receiver (接收者) | CID(类标识), OID(对象标识) | 指定操作的目标对象。可省略,默认为 Self(当前上下文对象)。 |
| Path (路径) | ->Link1->Link2.Item | 通过链接(link)导航到目标对象。可省略。 |
| Item (项目) | 方法(method)、字段(field)、链接(link) | 指定要操作的具体属性或方法。 |
| at_date(TE, LA) | at_date(Date_effect, Date_learn) | 指定操作在哪个时间点生效(TE)和何时被系统获知(LA)。 |
get:与 set: :最基础的操作,分别用于读取和写入对象的属性(字段)值。
m:与 getm: :都与调用方法相关。m:更侧重于执行一个动作或命令,可能不关心返回值;而 getm:则明确表示要获取方法执行后的结果。
t:与 : :通常用于类型检查或转换。例如,t:可能用于检查对象是否为某种类型。
x:这个指令通常有特殊含义,可能代表执行一个特定的流程,或者有系统预定义的独特行为,具体需参考系统文档
🛠️ 通用调用的实际应用
让我们通过几个假设的例子,将上述概念组合起来,看看通用调用在实战中如何运用。
| 示例调用 | 含义解释 |
|---|---|
get: Policy.12345.premium | 获取ID为12345的保单对象的保费字段当前值。 |
set: Policy.12345.status = "InForce" | 将ID为12345的保单对象的状态字段设置为“生效中”。 |
getm: Contract.67890->insured.calculateAge() | 调用ID为67890的合同所关联的被保险人的计算方法,以获取其年龄。 |
get: Self->parentContract.coverageLimit at_date(20241001, 20241001) | 获取当前对象的父合同在2024年10月1日生效且于同日获知的保额限制。 |
多次调用及其常见形式
| 组件 | 说明 | 类比 |
|---|---|---|
compute | 声明要进行的计算或要生成的集合。 | 类似于SQL中的 SELECT。 |
where | 定义计算所依赖的数据来源和生成规则。 | 类似于SQL中的 FROM和 WHERE。 |
m:/getm: | 在迭代中执行方法调用,getm:强调获取返回值。 | 类似于在循环中调用函数。 |
| 迭代变量 | 如示例中的 N和 Cntrct,代表集合中的当前元素。 | 类似于 for循环中的索引或元素。 |
您提到的“多次调用”是GT语言中一个非常强大的功能,它通过 m:或 getm:消息符与 compute ... where ...结构结合,实现了对集合数据的声明式批量处理。这类似于SQL语句或函数式编程中的映射(Map)操作。
示例1:计算总和
Sum = compute sum(M) where
(
N = m:type.enum_nbr(1, 12) // 迭代变量N,从1到12
& M = N * 3 // 为每个N计算 M = N * 3
)
执行过程:compute引擎会循环12次(N从1到12)。每次循环中,先通过 m:type.enum_nbr获取当前的N值,然后计算 M = N * 3。最终,将所有循环中计算出的M值进行求和(sum(M))。
- 结果:Sum 的值将是
3 + 6 + 9 + ... + 36 = 234。
示例2:生成对象列表
List = compute list(Res) where
(
Cntrct = m:obj.find($header:cp_contract, [status_1 = "terminated", nil) // 查找所有符合条件的合同
& Cvrg = m:lst.member(?get:Cntrct.cvrg_lst) // 获取每个合同关联的保障对象
& Res = [Cntrct, Cvrg] // 将合同和其对应的保障对象组合成一个元素
)
执行过程: m:obj.find方法会找到所有状态为 "terminated" 的合同对象,Cntrct作为迭代变量会遍历每一个找到的合同。 对于每一个合同(Cntrct),通过 m:lst.member和 ?get:Cntrct.cvrg_lst获取该合同下的保障列表中的一个成员(Cvrg)。这里可能意味着每个合同只取其一关联的保障,或者 cvrg_lst本身是单值。 将当前的合同 Cntrct和其对应的保障 Cvrg组合成一个子列表 Res。
compute list(Res)会将每次循环产生的 Res子列表收集起来,最终组合成你要的 List。
结果:List会是一个形如 [ [合同1, 保障1], [合同2, 保障2], ... ]的列表,非常适合用于后续的数据分析或报表生成
控制结构 ➔ 熟悉的流程控制
// Java的控制结构
if (condition) { ... }
for (int i = 0; i < 10; i++) { ... }
while (condition) { ... }
// GDL中极其相似!
IF condition THEN ... ENDIF
FOR i = 1 TO 10 ... NEXT i
WHILE condition ... ENDWHILE
明白您想聚焦于GT语言中控制结构的具体语法和用法。下面我用一个清晰的表格来总结这些核心结构,并附上详细的解释和示例。
| 控制结构 | 核心作用 | 关键特性 | 示例 |
|---|---|---|---|
compute … where … | 数据转换与集合构建 | 声明式地生成列表或聚合数据(如求和)。 | Sum = compute sum(M) where (N = m:type.enum_nbr(1,12) & M = N * 3) |
for_all … do … | 集合遍历与批量操作 | 对集合中的每个元素执行相同的操作。 | for_all CntrctNo = m:lst.member(Lst) do ( ... ) |
loop … define … do … | 带初始状态的累积循环 | 在循环开始前定义初始变量,并在循环中更新其状态。 | loop m:lst.member(WorkLst, CvrgCode = CvrgStatus) define (UWNum = 0) do ( ... ) |
while … do … | 条件循环 | 只要条件为真,就重复执行循环体。 | while t:file.read(FileRead, [CntrctNo]) do ( ... ) |
if…then…else | 双分支条件判断 | 根据单一条件的真假执行不同的代码路径。 | if Flag == true & ?get:Cntrct.status_1 == "in_force" then ( ... ) else ( ... ) |
case...then…else_if…then…otherwise… | 多分支条件判断 | 针对一个变量的不同可能值,执行对应的操作。 | case Num == 1 then ... else_if Num == 2 then ... otherwise nop |
1. 数据转换:compute … where …
这个结构非常强大,它允许你通过描述数据的来源和生成规则来“声明”一个新的列表或聚合结果(如求和),而不是一步步地“命令”计算机如何构建它
。引擎会根据 where子句中的规则自动迭代数据源,并将每次迭代产生的值收集起来。
示例分析:Sum = compute sum(M) where (N = m:type.enum_nbr(1,12) & M = N * 3) 执行过程:compute引擎会循环12次(N从1到12)。每次循环中,先通过 m:type.enum_nbr获取当前的N值,然后计算 M = N * 3。最终,将所有循环中计算出的M值进行求和(sum(M))。
- 结果:Sum 的值将是
3 + 6 + 9 + ... + 36 = 234。
2. 循环控制
GT语言提供了多种循环结构来应对不同的场景。
for_all … do …:专注于遍历
这个结构明确表达了“对集合中的每一个元素执行某操作”的意图
。它隐藏了索引管理等细节,让你更关注于业务逻辑本身。 示例分析:for_all CntrctNo = m:lst.member(Lst) do ( Cntrct = :obj.find($header:cp_contract, [idntfr == CntrctNo], nil) & set:Cntrct.status_1 = "terminated" )
执行过程:从列表 Lst中逐个取出合同编号 CntrctNo,并对每个编号执行查找合同对象并修改状态的操作。
loop … define … do:专注于累积
这个结构的关键在于 define块,它允许你在循环开始前初始化一个或多个“计数器”或“累加器”变量(如示例中的 UWNum)。在每次循环中,你都可以根据当前元素更新这些变量的状态,从而实现计数、求和等需要记忆中间结果的复杂逻辑 。
示例分析:在您的例子中,循环会对 WorkLst中的每个保障状态进行判断,如果状态为 "chk",则 UWNum加1,否则加0。循环结束后,UWNum的值就是状态为 "chk"的保障数量。
while … do …:专注于条件
当循环的次数不依赖于一个固定的集合,而是取决于某个动态变化的条件时(例如,持续从文件中读取数据直到文件结尾),while循环是最佳选择 只要条件为真,循环就会继续。
- 示例分析:
while t:file.read(FileRead, [CntrctNo]) do ( ... )会一直执行循环体,直到t:file.read无法再读取到新的合同编号为止。
3. 条件控制
条件控制决定了程序执行的路径。
if…then…else:简单直接的双路选择
这是最基本的分支结构,适用于只有两种可能性的情况
。如果 if后的条件为真,则执行 then块;否则,执行 else块。
case...then…else_if…then…otherwise…:清晰的多路选择
当需要根据一个变量的不同值执行不同操作时,case结构比一连串的 if...else if更清晰、更易于维护
。otherwise子句用于处理所有未明确列出的情况,保证逻辑的完备性。
库函数
| 函数类别 | 核心用途 | 代表性函数示例 |
|---|---|---|
| 角度与三角函数 | 处理角度转换和三角计算 | radians(), degrees(), sin(), cos(), tan() |
| 指数与对数函数 | 执行幂运算、对数运算等 | pow(), exp(), log(), exp2(), log2(), sqrt() |
| 常用数学函数 | 进行取整、取模、极值约束等常规操作 | abs(), sign(), floor(), ceil(), mod(), min(), max(), clamp() |
| 几何函数 | 计算向量长度、点积、叉积等 | length(), dot(), cross() |
| 插补运动函数 | 控制多轴协调运动,绘制路径 | GT_LnXY()(直线插补), GT_ArcXY()(圆弧插补) |
| 向量与矩阵运算 | 处理向量和矩阵的按分量或线性代数运算 | 按分量加减乘除, 矩阵乘法 (m * m) |
| 函数类别 | 代表性函数 | 核心用途 | 简单示例 | 返回值说明 |
|---|---|---|---|---|
| 对象操作 | :obj.find | 根据条件查找并返回对象。 | Cntrct = :obj.find($header:cp_contract, [idntfr == "C20241105001"], nil) | 找到的合同对象 |
:obj.get | 根据唯一标识符直接获取对象。 | Owner = :obj.get($individual:owner$67890) | 所有者对象 | |
| 列表处理 | :lst.member | 遍历列表中的每个成员。 | for_all Cvrg = :lst.member(CvrgLst) do (...) | 列表中的当前成员 |
:lst.append | 向列表尾部添加新元素。 | NewLst = :lst.append(OldLst, NewElement) | 新列表 | |
| 数学运算 | :type.enum_nbr | 生成指定范围的数字序列。 | N = :type.enum_nbr(1, 12) | 数字序列,如1,2,3,...,12 |
sum, min, max, avg | 在 compute结构中对集合进行聚合计算。 | Total = compute sum(M) where (M = ...) | 计算结果值 | |
| 类型转换 | str | 将其他类型的数据转换为字符串。 | Msg = "合同号:" + str(CntrctNo, 1, 0) | 字符串 |
int | 将字符串或浮点数转换为整数。 | Age = int("26") | 整数 |
方法
无论是什么编程语言,使用库函数通常都遵循一个核心流程:确认函数存在 → 准备输入数据 → 调用函数并处理结果 → 错误处理。下表详细说明了每个步骤的操作和要点。
| 步骤 | 核心任务 | 操作与要点 |
|---|---|---|
| 1. 确认函数 | 确认GT环境提供了你需要的函数。 | • 查阅官方文档:这是最权威的途径,寻找GT语言的官方手册或API参考。 • 社区与示例:在开发者社区、论坛或示例代码中寻找常用函数列表。 • 内部查询:某些GT环境可能支持通过特定命令(如 :help或 :list_functions)列出可用函数。 |
| 2. 准备输入 | 根据函数要求,准备好格式和类型都正确的参数。 | • 参数类型:确保你准备的数据(如字符串、数字、列表)与函数定义的参数类型匹配。 • 数据格式:特别注意是否需要特定的格式,比如日期字符串的格式(YYYYMMDD)。 • 必选/可选参数:分清哪些参数是必须提供的,哪些有默认值可以省略。 |
| 3. 调用与处理 | 使用正确的语法调用函数,并接收、检查和使用返回值。 | • 调用语法:通常为 函数名(参数1, 参数2, ...)。注意GT语言可能使用前缀,如 m:, t:。 • 接收返回值:使用变量接收函数结果,例如 Result = m:math.sqrt(MyNumber)。 • 检查结果:调用后立即检查返回值是否有效,避免基于错误结果进行后续操作。 |
| 4. 错误处理 | 预料并妥善处理函数调用中可能出现的错误。 | • 检查错误码:某些函数在出错时可能返回一个特殊值(如 -1或 nil)。 • 查看错误信息:GT系统可能会提供错误描述,帮助定位问题。 • 使用条件判断:用 if或 case语句判断函数执行是否成功,再决定后续流程。 |
由于缺少GT语言的确切函数名,以下示例将模拟几种常见场景,展示其可能的代码形态和逻辑,供您参考。
| 场景 | 模拟代码示例 | 代码解读 |
|---|---|---|
| 数学计算 | Distance = m:math.sqrt( (X2 - X1)^2 + (Y2 - Y1)^2 ) | 假设存在 m:math.sqrt函数计算平方根,用于计算二维平面上的两点距离。 |
| 字符串处理 | FullName = t:string.concat(FirstName, " ", LastName) | 假设存在 t:string.concat函数,用于将姓氏、空格和名字连接成一个完整的姓名字符串。 |
| 列表操作 | FilteredList = m:list.filter(OriginalList, "status_1 == 'active'") | 假设存在 m:list.filter函数,根据条件(如状态为“active”)过滤一个列表。 |
| 日期处理 | DaysDiff = m:date.diff(Date1, Date2, 'days') | 假设存在 m:date.diff函数,计算两个日期之间相差的天数。 |
1. 方法原型(接口)定义
这就像是方法的“使用说明书”,告诉使用者需要提供什么,以及会得到什么结果。
// 方法原型:声明一个名为 compute_tax 的方法
void compute_tax(in ident Status, in real Salary, out real Tax)
- 解读:这个方法需要调用者输入(
in)一个标识符类型的Status(如婚姻状态)和一个实数类型的Salary(收入),方法执行后会输出(out)一个实数类型的Tax(税额)。void表明方法主要目的是通过out参数输出结果,自身不另返回值。
2. 方法体(具体实现)
这是方法功能的具体实现,通常包含逻辑判断。
// 方法体:实现 compute_tax 的具体逻辑
compute_tax(married, S, T) :- ! & compute_m(S, T); // 如果状态是"married",则调用compute_m计算
compute_tax(single, S, T) :- ! & compute_s(S, T); // 如果状态是"single",则调用compute_s计算
compute_tax(_, S, T) :- T = S * 0.01; // 默认情况(其他状态)按收入的1%计算
3. 本地断言(辅助方法)
在方法内部,可以定义更小的、可复用的“本地断言”来完成具体计算,类似于辅助函数。
// 定义本地断言,计算已婚状态的税
compute_m(X, Y) :- Y = X * 0.1; // 已婚税率为10%
// 定义本地断言,计算单身状态的税
compute_s(Z, S) :- S = Z * 0.25; // 单身税率为25%
4. 调用方法
定义好方法后,就可以通过方法名和实际参数来调用它了。
// 调用 compute_tax 方法,传入实际参数
compute_tax(married, 50000.0, MyTax)
// 此时,变量 MyTax 将被赋值为 5000.0