GT语言使用

77 阅读21分钟

gt语言和gtl区别是什么?

GT语言

GT语言并不是一个单一明确的编程语言,而是有多种可能的含义:

  1. GNU Data Language (GDL)

全称:GNU Data Language

性质:开源的数据处理和可视化工具

兼容性:与IDL(Interactive Data Language)和PV-WAVE兼容

应用领域:科研、天文、地球科学、医学成像等

特点:动态类型、数组导向、支持向量化操作、面向对象编程能力

功能:提供丰富的库函数,涵盖数值计算、图形化展示、信号与图像处理等

  1. 通用描述语言 (Generic Description Language)

性质:形式语言,用于描述语言的语法规则

应用:语音识别、自然语言处理、编译器开发

功能:定义语言中允许的词语序列、短语结构和句子结构

  1. 图形化编程技术 (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__edno__edx定义这个状态在业务上有效的时间范围,同时通过 o__kdno__kdx跟踪这个记录片段本身在系统中的生命周期。o__kdxa999999这样的标志则用于将这些片段在时间线上正确地链接起来。

假设我们要记录客户“会员等级”的变化:

第一天(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日期及历史管理

功能领域核心概念/操作简要说明
日期与时间获取系统日期使用 DATETIME等保留变量直接从操作系统获取当前日期和时间。
自定义日期变量定义变量存储特定日期,用于参数化控制(如模型版本日期)。
日期格式转换使用 STR等函数将日期数值格式化为特定格式的字符串。
历史管理参数化驱动通过修改关键参数(如日期、尺寸)的值,回溯或生成不同历史状态的设计。
脚本逻辑控制利用 IF...THENFOR等控制结构,根据不同日期条件执行不同几何生成操作。
版本注释在脚本中添加注释,记录重要历史更改的原因、日期和作者。

💡 日期处理基础

在GDL中处理日期,通常涉及以下几个方面:

系统日期获取:GDL可以通过一些保留变量或内置函数来获取当前的系统日期和时间。这常用于在模型中自动标记创建日期或时间戳。

自定义日期变量:你可以定义变量来存储特定的日期。例如,可以创建一个变量 Project_StartDate并将其设置为一个特定的日期值,用于控制与项目开始日期相关的参数或显示。

日期格式转换:使用如 STR等函数,可以将日期数值转换为特定格式的字符串,以便于在模型的标签或注释中显示。

🔄 历史管理思路

GDL本身不像数据库那样自动记录每一步操作历史,但其参数化和脚本化的特性为管理设计历史提供了强大灵活性:

参数化驱动历史回溯:这是最核心的方法。通过修改控制模型的关键参数(例如一个代表“设计阶段”的日期参数),可以立即将模型回溯到该日期对应的状态,或者生成不同历史版本的设计方案。

脚本逻辑控制版本:在GDL脚本中,可以使用 IF...THENSELECTFOR等控制结构,根据不同的日期或版本参数值,执行不同的几何生成逻辑、加载不同的材质或显示不同的文本注释。这使得一个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_VERSIONGDL和库版本信息
系统信息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]中的匹配结果
`[HT]`将列表分为第一个元素(头)和剩余所有元素(尾)
`[A, B, C_]`匹配列表的前三个元素,并忽略剩余部分(_
`[HT](当L = [a]`)单元素列表:头是该元素,尾是空列表 []
`[HT](当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`[HT]`
Pythonlist[0], list[1:]通过索引获取第一个元素和切片获取剩余部分,但这不是模式匹配。
Javalist.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为 1234cp_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中的 FROMWHERE
m:/getm:在迭代中执行方法调用,getm:强调获取返回值。类似于在循环中调用函数。
迭代变量如示例中的 NCntrct,代表集合中的当前元素。类似于 for循环中的索引或元素。

您提到的“多次调用”是GT语言中一个非常强大的功能,它通过 m:getm:消息符与 compute ... where ...结构结合,实现了对集合数据的声明式批量处理。这类似于SQL语句或函数式编程中的映射(Map)操作。

示例1:计算总和

Sum = compute sum(M) where
      (
           N = m:type.enum_nbr(1, 12)  // 迭代变量N,从112
           & 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, avgcompute结构中对集合进行聚合计算。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. 错误处理预料并妥善处理函数调用中可能出现的错误。检查错误码:某些函数在出错时可能返回一个特殊值(如 -1nil)。 • 查看错误信息:GT系统可能会提供错误描述,帮助定位问题。 • 使用条件判断:用 ifcase语句判断函数执行是否成功,再决定后续流程。

由于缺少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