首页/文章/ 详情

【HyperMesh宝典】二次开发之 Tcl 中的变量

8月前浏览2885

本文摘要(由AI生成):


二次开发的教程已经出了两期了,大家是不是对二次开发的了解越来越深入了呢?从这一讲开始,我们要越来越多地和 HyperMesh 打交道啦。

通过上一期的基础培训,相信大家应该了解了一些编程的基本概念了吧,比如变量,字符串,if 和 for 等等。如果忘记了赶紧点击下方链接复习一下上期内容吧~  


首先要声明一下,如果有同学希望通过复 制粘贴来实践一下本文中的代码,记得将双引号删掉再重新输入一下。原因是office中的双引号不是标准的英文双引号。下文中很多代码都是以图片形式给出的,需要自己重新敲一下。代码需要实践后才能真正理解,化为己用哟~

此外,上一期的小贴士不知道大家还记得吗?为了方便大家识别,我们把输入部分放在%后面,把输出放在=> 后面,这篇教程也同样适用哈。


 


言归正传,一般的编程语言中有很多种数据类型:字符、字符串、布尔型、整型、实型、数组、结构、类等等,而且一种变量还分成很多子类,比如整型又分为普通整型、长整型、枚举型,普通整型又分为带符号的和不带符号的等等。

Tcl中的变量也可以分为以下几种类型:字符串、整型、实型、列表、数组、字典。

是不是感觉看起来很复杂?其实也不尽然,下面我们通过举例详细说明一下~


Tcl 用 set 命令定义变量

► 字符串:% set name HyperWorks

► 整型: % set num 3

► 实型: % set num 3.2

► 数组: % set hw(pre) HyperMesh

► 字典: % set dict1 [dict create pre hypermesh post hyperview solver RADIOSS solvers optistruct]

但是在脚本层面上你可以认为 Tcl 只有一种数据类型,就是字符串。

例如下面这条命令定义了一个变量,变量的值是 a 1 b 2 c 3:

% set var1 {a 1 b 2 c 3}

那么现在的var1这个变量里面到底存储的是什么类型的数值呢?先不急着回答,我们先用几个命令来试试~


% string range $var1 0 3

=> a 1


► 结论1:var1是字符串类型

% lindex $var1 1

=> 1

► 结论2:var1是列表类型

% dict get $var1 a

=> 1

► 结论3:var1是字典类型

 

WHAT?!既是字符串又是列表又是字典?什么情况?

因为 Tcl 是弱类型的编程语言在 Tcl 中一切都是字符串,必要的时候会自动转换成其它相应的类型。弱类型的好处是编程具有很大的灵活性,付出的代价是程序跑得又慢占用的内存又多而且出错的可能性也更大。好消息是,Tcl 语言的开发者也知道这个情况,这就是上一讲中为什么让大家把 expr后面的表达式放在 { } 里面的原因,这样可以减少数据类型转换,加快计算速度。

一般的编程语言中的变量名只允许字母数字和下划线,但是 Tcl 不受此限制。(你可以随意给变量取名,但这通常是个非常坏的主意!)

% set abc 123

=> 123

% set 123 abc

=> abc

% set {a b c} 333

=> 333

► 判断普通变量是否存在使用 info exist

% info exist {a b c}

=> 1

% puts ${a b c}

=> 333

 删除变量使用 unset,如果直接使用未定义的变量,会报错。

% unset abc

% puts $abc

=> can't read "abc": no such variable

► 如果同时定义3个变量 comp_1、comp_11 和 {comp_1 1}

% set comp_1 shell

% set comp_11 solid

% set {comp_1 1} mix

那么

% puts $comp_1

% puts $comp_11

% puts “$comp_1 1”

分别会输出什么结果呢,我们来看看:

% puts $comp_1

=> shell

% puts $comp_11

=> solid

% puts “$comp_1 1”

=> shell 1

► 如果comp_11未定义呢?下面哪条命令可行?不妨自己试一试,失败是成功之母哟,也正好考察考察你是否理解了上一讲的内容~

% puts $comp_1 1

% puts ${comp_1 1}

% puts $comp_1\ 1

% set var_name {comp_1 1}; puts $$var_name

% set var_name “comp_1 1”; puts $[set var_name]

% set var_name “comp_1 1”; puts [set [set var_name]]

► 为了避免类似问题,当不确定时就把变量名放在 { } 里面(但是如果里面有变量就不能这么干了,最好用数组或者字典替代)。


列表类型

接下来介绍列表类型,这是最最最重要的类型。因为 HyperMesh 几乎总是返回列表,同时列表也是用起来最方便的一种数据类型。

针对一个变量的操作通常包括创建,删除,查询(是否存在),修改(或者部分修改)以及遍历等。当然,不同类型之间会有一些差别。

列表是什么?

列表就是有顺序的一列数据。


01

创建一个列表变量

➡ 1.简单的列表常量可以用 { } 定义

% set lst {1 2 3}

➡ 2.一般建议用 list 命令定义

% set lst [list 1 2 3]

列表可以嵌套:

% set lst2 [list {1 2 3} {4 5 6} {a b c}]

% lindex $lst2 1

=> 4 5 6

% lindex $lst2 1 1

=> 5

用过 python 的同学可能都会比较遗憾 tcl 里面没有 range 函数。

别急,我们可以自己发明一个!

 

(点击图片查看高清大图)


➡ 3. 之前提到过,大部分情况下 HyperMesh 的 API 直接把列表作为命令的输出给你了,这时你就不必自己创建了,是不是很棒?

下一讲我们将介绍怎么使用 HyperMesh 的 API 得到想要的数据,请持续关注公众 号哟~

 

➡ 4. 最后还有一种创建的方法是用 split 命令将字符串转变为列表:

% set string1 “HyperMesh HyperView HyperGraph”

% set newlst [split $string1]

 同样的,删除列表变量也是unset命令。


02

列表的常用操作


➡ 1. 从列表取数据

► llength 命令可以获取列表里有多少个元素,如果是多级列表只对第一级进行查询。

► 取一个列表元素用 lindex

% set lst2 [list {1 2 3} {4 5 6} {a b c}]

% lindex $lst2 1 1

等价于

% lindex [lindex $lst2 1] 1

等价的下面这一句是不是明显简洁多了?取一段数据用 lrange,用得不多,就不展开讲了。


➡ 2.对列表进行排序

► 一般使用 lsort ,它非常高效而且有很多功能强大的选项,比如 -unique 选项可以筛掉重复元素。其中 特别重要的是 -command 选项,它可以实现自定义规则的排序,比如将 HyperMesh 模型中的部分节点或者单元按照到原点的距离进行排序(代码见下图)。

► 这个自定义函数接受两个参数(a,b),返回值1(a>=b), -1(a<b)

 

上图第11~12行的目的是获取用户框选的节点id号,函数还没有讲到看不懂就先跳过吧。运行情况如下(图中黄色的节点就是原点位置):

 
 

► 此外还有一个专门的函数 lreverse 用于反转列表:

% lreverse {O K}

=> K O


➡ 3.修改现有列表

► 在列表后面追加数据的速度很快,但是要在开始或中间增加或删除一个就要慢得多,这也是我们经常在程序中看到 lappend 的原因。

% puts time {lappend lst1 9}

=> 23 microseconds per iteration

% puts time {linsert $lst1 9 10000}

=> 35403 microseconds per iteration

► 上面的 $lst1 是一个用 range 函数创建的100万个元素的列表,linsert 花费的时间大约是 lappend 的1500倍,而且列表越长,差别越大。

► lset 直接修改列表中的某一个元素,lreplace 和 linsert 用法也是类似的,这些命令平时用到的都不多可以留待大家课后自学。

% set lst {1 2 3}

% lset lst 1 9;把第一个元素改成9,别忘了tcl列表的下标也是从0开始的哦

=> 1 9 3


➡ 4.连接两个列表

► 方法一:concat

第一层是一个包含6个元素的列表,第二层的每个元素是具有3个元素的列表。

% set lst1 [list {1 2 3} {4 5 6} {7 8 9}]

% set lst2 [list {a b c} {d e f} {g h p}]

% concat $lst1 $lst2

=> {1 2 3} {4 5 6} {7 8 9} {a b c} {d e f} {g h p}

► 方法二:list

% list $lst1 $lst2

=> {{1 2 3} {4 5 6} {7 8 9}} {{a b c} {d e f} {g h p}}

这里的 $lst1 是 {{1 2 3} {4 5 6} {7 8 9}}, $lst2是{{a b c} {d e f} {g h p}} 。

list 和 concat 的区别:

list 命令就是把成员用一个 { } 包起来,concat 就是把两个 list 的最外层 { } 先剥掉然后再把所有成员用 { } 包起来。list 命令和 concat 的差别就是 list 命令不给成员脱衣服了,直接用 { } 把他们给包了起来~

► 理解列表结构

第一个输出中的

{1 2 3} {4 5 6} {7 8 9} {a b c} {d e f} {g h p}

实际上是

{{1 2 3} {4 5 6} {7 8 9} {a b c} {d e f} {g h p}}

第一层是一个6个元素的列表(也就是剥掉一层大括号),第二层的每个元素又是一个具有三个元素的列表。

第一个输出中的

{{1 2 3} {4 5 6} {7 8 9}} {{a b c} {d e f} {g h p}}

实际上是:

{ {{1 2 3} {4 5 6} {7 8 9} } { {a b c} {d e f} {g h p} } }

第一层是一个2个元素的列表,分别是 {{1 2 3} {4 5 6} {7 8 9}} 和 {{a b c} {d e f} {g h p}},

第二层每个元素又是一个具有3个元素的列表,分别是 {1 2 3} 和 {4 5 6} 和 {7 8 9} ,

第三层每个元素又是一个具有3个元素的列表,分别是1 和 2 和 3。

理解列表的结构非常关键,比如下方这条HyperMesh的命令(4365是某节点的id号)。

% hm_nodevalue 4365

输出如下:

 

那我们如果想得到x坐标是不是可以使用lindex $coor 0呢?

 

答案是不行,问题的原因是 HyperMesh 自动把最外层的{}去掉了,如果你看到有一层 { } ,那么实际上就是有两层 { },从下面的简单示例也看得出来。

 

总而言之,定义的时候你需要加上最外层 { } ,显示时 HyperMesh 会自动把最外层的 { } 去掉。

再往下看你会发现我们自定义的 flatten 非常乐意为你解除这个烦恼。

 


03

使用列表对多个变量进行赋值

(这样至少可以少写几行代码)

% set lst {1 2 3}

lassign $lst x y z

puts “x=$x y=$y z=$z”

=> x=1 y=2 z=3

来个复杂一点的

% set lst123 [list {1 2 3} {4 5 6} {7 8 9}]

% lassign $lst123 v1 v2 v3

% puts “v1=$v1 v2=$v2 v3=$v3”

=> v1=1 2 3 v2=4 5 6 v3=7 8 9

有没有被忽悠的感觉?

 

有没有办法一下子把123456789分别赋给9个不同的变量呢?

tcl没有现成的功能,不过我们还是可以自己写一个函数。这个函数简单到难以置信。

快接着往下看!

 

然后就可以执行下面的操作,把一个嵌套列表拍平,也就是把所有 { } 剥掉然后再用 { } 包起来。

% set nestedlist {1 {2 3} {4 {5 6}} {{7 8 9}} {10} {}}

% set flattened [flatten $nestedlist]

=> 1 2 3 4 5 6 7 8 9 10

然后就简单了:

% lassign $flattened v1 v2 v3 v4 v5 v6 v7 v8 v9

% puts “v1=$v1 v2=$v2 v3=$v3 v4=$v4 v5=$v5 v6=$v6 v7=$v7 v8=$v8 v9=$v9”

当然,你也可以用一个循环来达到相同的目的,但是使程序便于理解和易于维护的关键就是——不要有太多的循环和嵌套,用一条命令代替一个循环,完美!

再重复一遍,不要有太多的循环和嵌套~

所以接下来我们介绍最后一个列表的命令 lmap 。这个命令在 tcl 8.6 的版本才加入的,而目前的 HyperMesh 对应的 tcl 版本是8.5,所以,很抱歉不支持该命令。

但是,不要捉急,我们依然可以自己发明一个。下面的这段代码来自wiki,有点抽象,睁大眼睛,争取看懂它哦。


 

接下来你就可以进行这样的命令

set res [lmap x {1 2 3} y {10 20 30} {expr {$x*$y}}]

如果使用循环的等价命令为:

 


这一讲到这里就结束啦~这期是不是含金量超级高呢~ 感谢看到这里的你,更感谢辛苦付出的教程作者方老师~(明年会有明星讲师神秘揭面哦~~期待ing...)


 


下一讲我们将介绍如何在HyperMesh中得到各种列表数据。至于数组和字典只能等到下下期了。不过我们正在努力加快更新的速度哟!(后台催稿的那些朋友,不要太激动哈,拥抱点赞什么的尽管砸来~)


 


来源:Altair澳汰尔
RADIOSSOptiStructHyperMeshHyperView二次开发HyperWorks设计与仿真平台
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2024-03-27
最近编辑:8月前
Altair澳汰尔
澳汰尔工程软件(上海)有限公司
获赞 142粉丝 476文章 746课程 4
点赞
收藏
作者推荐
未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习 福利任务 兑换礼品
下载APP
联系我们
帮助与反馈