tlc与sfunction(四)TLC概述

介绍:

理解目标语言编译器(TLC)的最快、最简单的方法是运行它,注意TLC脚本如何将编译的Simulink模型(model.rtw文件)转换为源代码。本章中的教程旨在强调使用TLC的主要原因和技术。教程提供了许多TLC练习,每个练习都被组织成一个主要部分。
练习所需的所有示例模型、S函数和TLC文件都位于matlabroot/toolbox/rtw/retwdemos/tlctutorial中,其中matlabroot是系统上的MATLAB根文件夹。在本章中,此文件夹称为tlctutorial。每个示例都位于tlctutorial中的单独子文件夹中。在该子文件夹中,您可以在解决方案子文件夹中找到问题的解决方案。

注意:在开始本教程之前,请将整个tlctutorial文件夹复制到本地工作文件夹中。所有需要的文件都在一起,如果你犯了错误或想重新尝试新的示例,你可以从原始的tlctutorial文件夹中重新复制文件。

每个教程练习的范围都是有限的,只需要少量的实验。本教程详细解释了TLC,这将有助于为实时研讨会项目定制和优化代码。

注意:即使存在这样做的功能,也不应自定义matlabroot/rtw/c/TLC文件夹中的TLC文件。此类TLC自定义可能不会在代码生成过程中应用,并可能导致不可预测的结果。

教程的难度从基础到更高级。为了充分利用它们,你应该熟悉

•在MATLAB环境中工作
•构建Simulink模型

•使用rtw为目标系统生成代码
•高级语言概念(例如,C或Fortran编程)

如果你在教程中遇到你不理解的术语,阅读“代码生成概念 ”章节可能会有所帮助,以熟悉TLC编程的基本目标和方法。同样,如果你看到TLC关键字、内置函数或你想了解更多的指令,请参阅MATLAB的帮助文档。

本教程中使用的示例包括:

例子   说明
guide说明性记录文件
timesN一个将输入乘以N的C文件S函数示例
tlcdebug使用TLC调试器的示例
wrapperwrapper .c 的sfunction TLC文件示例

 1.用TLC读取记录文件

目的:了解记录文件的结构,并学习如何使用TLC指令解析它们。
文件夹:tlctutorial/guide

在本教程中,您将使用一系列TLC脚本解释一个简单的结构化记录文件。您将了解记录的结构,以及如何使用TLC%分配和%<>令牌扩展指令来处理它们。此外,本教程还演示了使用%foreach的循环和使用%with的作用域。
本教程包括以下步骤,您应该按顺序执行:

  1. 学习记录文件的结构——一些背景和一个简单的例子
  2. 解释记录——呈现记录文件的内容
  3. 学习TLC脚本的结构——解构演示文稿
  4. 修改read-guide.TLC——实验TLC
  5. 传递和使用参数——将参数从命令行传递到TLC文件
  6. 复习

1.1学习记录文件的结构

rtw代码生成器将模型编译成一种称为记录文件的结构化形式,称为model.rtw。这种编译的模型文件在语法和组织上与源模型(model.mdl)文件相似,因为它们包含一系列分层嵌套的记录

recordName {itemName itemValue}


项目名称itemName 是按字母顺序排列的。

项目值itemValue可以是字符串或数字。数值可以是标量、向量或矩阵。

大括号分隔每条记录的内容,其中可能包含一个或多个项目,由空格、制表符或返回字符分隔。

在model.rtw文件中,顶级(第一个)记录的名称是CompiledModel。每个块由其中的子记录表示,子记录由块的名称标识。
TLC可以解析任何格式良好的记录文件,如本练习所示。
以下列表是TLC可以解析的有效记录文件,但不是可以生成代码的文件。注释用符号(#)表示:

# $Revision: 1.1.4.37 $
# File: guide.rtw Illustrative record file, which can't be used by Simulink
# Note: string values MUST be in quotes
Top { # Outermost Record, called Top
    Date "21-Aug-2008" # Name/Value pair named Top.Date
    Employee { # Nested record within the Top record
        FirstName "Arthur" # Alpha field Top.Employee.FirstName
        LastName "Dent" # Alpha field Top.Employee.LastName
        Overhead 1.78 # Numeric field Top.Employee.Overhead
        PayRate 11.50 # Numeric field Top.Employee.PayRate
        GrossRate 0.0 # Numeric Field Top.Employee.GrossRate
    } # End of Employee record
    NumProject 3 # Indicates length of following list
    Project { # First list item, called Top.Project[0]
        Name "Tea" # Alpha field Name, Top.Project[0].Name
        Difficulty 3 # Numeric field Top.Project[0].Difficulty
    } # End of first list item
    Project { # Second list item, called Top.Project[1]
        Name "Gillian" # Alpha field Name, Top.Project[1].Name
        Difficulty 8 # Numeric field Top.Project[1].Difficulty
    } # End of second list item
    Project { # Third list item, called Top.Project[2]
        Name "Zaphod" # Alpha field Name, Top.Project[2].Name
        Difficulty 10 # Numeric field Top.Project[2].Difficulty
    } # End of third list item
} # End of Top record and of file

只要程序员知道记录和字段的名称及其预期内容,他们就可以编写TLC语句来读取、解析和操作记录文件数据。

1.2解释记录

以下是TLC程序脚本的输出,该脚本读取guide.rtw,解释其记录,操纵字段数据,并格式化描述,这些都指向MATLAB命令窗口:

使用TLC你可以

1)直接访问任何字段的值,例如。

%<Top.Date>
结果是:“21-Aug-2008"
2)将字段的内容分配给变量,例如

%assign worker=Top.Employee.FirstName

将Top.Employee.FirstName这个内容赋值给worker这个变量

3)连接字符串值,例

%assign worker = worker + " " + Top.Employee.LastName

将字符串 worker 和Top.Employee.LastName连起来,并赋值给worker

4)执行算术运算

%assign wageCost = Top.Employee.PayRate * Top.Employee.Overhead

wageCost等于 Top.Employee.PayRate * Top.Employee.Overhead,即11.5*1.78=20.47

5)将变量放入字段

Top.Employee.GrossRate 初始化值为 0.0

%assign Top.Employee.GrossRate = wageCost

Top.Employee.GrossRate赋值为wageCost = 20.47

6)值索引列表

%assign projects = Top.Project[0].Name + ", " + Top.Project[1].Name...

"+ ", " + Top.Project[2].Name

projects的值为Top.Project[0].Name + ", " + Top.Project[1].Name"+ ", " + Top.Project[2].Name

即Tea, Gillian, Zaphod

7)通过循环遍历和操作列表数据,例如

- At top of Loop, Project = Tea; Difficulty = 3
- Bottom of Loop, i = 0; diffSum = 3.0
- At top of Loop, Project = Gillian; Difficulty = 8
- Bottom of Loop, i = 1; diffSum = 11.0
- At top of Loop, Project = Zaphod; Difficulty = 10
- Bottom of Loop, i = 2; diffSum = 21.0

平均Project 难度等于 diffSum / Top.NumProject = 21.0 / 3 = 7.0

guide.rtw的输出是通过从MATLAB命令窗口调用TLC,执行名为read-guide.TLC的脚本生成的。现在按照以下步骤自己做:

1在MATLAB中,将文件夹(cd)更改为工作文件夹中tlctutorial/guide的副本。
2要生成刚才列出的输出,请键入以下命令,使用TLC脚本read-guide.TLC处理guide.rtw:

tlc -v -r guide.rtw read-guide.tlc

注意命令用法:
•需要-r 开关(用于读取)来标识输入数据文件,在本例中为guide.rtw。
•处理数据文件的TLC脚本由最后键入的令牌指定。
•除非TLC文件本身处理,否则需要使用-v开关(用于详细)将输出定向到命令窗口。

1.3学习TLC脚本的结构

现在,您可以剖析刚刚运行的脚本。guide.tlc输出的每个“段落”在以下简短章节中按顺序进行讨论:

1.3.1 常用语法与编码规定

以下是一些基本的TLC语法和编码约定:

%%                        注释TLC注释,未输出
/*comment*/                评论,待输出
%keyword                TLC指令(关键字),以“%”开头
%<expr>                TLC令牌运算符
.(period)                范围界定运算符,例如Top.Lev2.Lev3
…(在行尾)        语句继续(无换行输出)
\(在行尾)        语句继续(输出换行符)

localvarIdentifier局部变量以小写开头

GlobalvarIdentifier全局变量以大写开头

RecordIdentifier记录标识符以大写开头

EXISTS()        TLC内置函数以大写命名

注意:所有TLC标识符都区分大小写。

有关更多信息,请参阅“TLC编码惯例”

1.3.2 文件头

文件read-guide.tlc以以下内容开头:

%% File: read-guide.tlc (This line is a TLC Comment, and will not print)
%% $Revision: 1.1.4.37 $
%% To execute this file, type: tlc -v -r guide.rtw read-guide.tlc
%% Set format for displaying real values (default is "EXPONENTIAL")
%realformat "CONCISE"

•第1行到第4行——字符%%后面的所有文本都被视为注释(忽略、不解释或输出)。
•第5行——如第四行文本所述,是TLC指令(关键字)%realformat,它控制后续浮点数在输出中显示时的格式。在这里,我们想尽量减少显示的数字。

1.3.3 符号Token扩展

输出的第一部分由脚本行生成:

使用TLC,您可以:
*直接访问任何字段的值,例如。
 

%assign td = "%" + "<Top.Date>"

%<td>-- evaluates to:

%<Top.Date>

第一行创建一个名为td的变量并分配字符串值%<Top.Date>指向它。%assign指令创建新变量并修改现有变量。它的一般语法是:

%assign ::variable = expression
可选的双冒号前缀指定分配给的变量是全局变量。如果没有,TLC会在当前范围内创建或修改局部变量

%<td.Date> -- evaluates to:
这一行TLC能够打印%<Top.Date>不展开它。它通过将两个字面值粘贴在一起来构造字符串。

%assign td = "%" + "<Top.Date>"
如“字符串处理+”章节中所述,加号运算符将字符串连接为数字、向量、矩阵和记录,并将其相加。
•第3行——评估(扩展)Top.Date。更确切地说,它评估作用域Top中存在的字段Date。

语法%<expr>
导致表达式expr(可以是记录、变量或函数)被求值。此操作有时被称为eval。

注意:您不能嵌套%<expr>运算符(即不允许%<foo%<bar>>)。

注意:当您在引号内使用%<expr>运算符时,例如“%<Top.Date>”,TLC会展开表达式,然后将结果括在引号中。但是,将%assign放在引号内,例如“assign foo=3”,只会将引号中的语句回声到输出流中。没有赋值结果(foo的值保持不变或未定义)。

1.3.4 一般任务

输出的第二部分由脚本行生成:

* Assign contents of a field to a variable, e.g.
%assign worker = Top.Employee.FirstName
"%assign worker = Top.Employee.FirstName"
worker expands to Top.Employee.FirstName = %<worker>

第1行,直接输出字符流

第2行 将Top.Employee这个记录范围的FirstName字段赋值给一个本地的worker变量

第3行 重复前面的语句,通过将其括在引号中来产生输出文字

第4行 先打印解释性的语句“worker expands to Top.Employee.FirstName =
”,然后打印符号扩展。符号%<worker>扩展为Arthur。

1.3.5 字符串处理增强版

脚本的下一节说明了字符串连接,这是“+”运算符的用法之一:

* Concatenate string values, e.g.
%assign worker = worker + " " + Top.Employee.LastName
"%assign worker = worker + " " + Top.Employee.LastName"
worker expands to worker + " " + Top.Employee.LastName = "%<worker>"

•第1行——直接字符串输出:* Concatenate string values, e.g.。
•第2行——执行连接。
•第3行——将第2行与输出相呼应,直接输出字符串"%assign worker = worker + " " + Top.Employee.LastName”。
•第4行——描述将变量连接到由空格字符分隔的字段的操作。

另一种不使用+运算符的方法是

%assign worker = "%<Top.Employee.FirstName> %<Top.Employee.LastName>"

这种做法同样有效。
+运算符是关联的,也适用于数值类型、向量、矩阵和记录:

数字类型——将两个表达式相加;两个操作数都必须是数字。例如:

* Numeric Type example, e.g.
Top.Employee.PayRate = %<Top.Employee.PayRate>
Top.Employee.Overhead = %<Top.Employee.Overhead>
%assign td = Top.Employee.PayRate + Top.Employee.GrossRate 
td = Top.Employee.PayRate + Top.Employee.Overhead
td evaluates to %<td>

输出结果
* Numeric Type example, e.g.
Top.Employee.PayRate = 11.5

Top.Employee.Overhead = 1.78

td = Top.Employee.PayRate + Top.Employee.Overhead

td evaluates to 13.28

向量类型——如果第一个参数是向量,第二个参数是标量值,TLC会将标量值附加到向量上。例如:

* Vector example, e.g.
%assign v1 = [0, 1, 2, 3] v1 is %<v1>
%assign tp1d = Top.Project[1].Difficulty Top.Project[1].Difficulty is %<tp1d>
%assign v2 = v1 + tp1d 
v2 = v1 + Top.Project[1].Difficulty 
v2 evaluates to: %<v2>

输出结果

* Vector example, e.g.
v1 is [0, 1, 2, 3]

Top.Project[1].Difficulty is 8

v2 = v1 + Top.Project[1].Difficul

v2 evaluates to: [0, 1, 2, 3, 8]

矩阵类型——如果第一个参数是矩阵,第二个参数是与矩阵列宽相同的向量,TLC会将向量作为另一行附加到矩阵中。例如:

* Matrices example, e.g.
%assign mx1 = [[4, 5, 6, 7]; [8, 9, 10, 11]] 
mx1 is %<mx1>
v1 is %<v1>
%assign mx = mx1 + v1 
mx = mx1 + v1 
mx evaluates to %<mx>

输出结果:

* Matrices example, e.g.
mx1 is [ [4, 5, 6, 7]; [8, 9, 10, 11] ] 
v1 is [0, 1, 2, 3] 
mx = mx1 + v1 
mx evaluates to [ [4, 5, 6, 7]; [8, 9, 10, 11]; [0, 1, 2, 3] ]

记录类型——如果第一个参数是记录,TLC会添加第二个参数作为参数标识符(及其当前值)。例如:

* Record example, e.g.
%assign StartDate = "August 28, 2008"
StartDate is %<StartDate>
%assign tsd = Top + StartDate
Top + StartDate
Top.StartDate evaluates to %<Top.StartDate>

输出结果:

* Record example, e.g.
StartDate is August 28, 2008
Top + StartDate
Top.StartDate evaluates to August 28, 2008

1.3.5 算数运算符

TLC为数值数据提供了完整的算术运算符。在TLC脚本的下一部分中,将两个数字字段相乘:

Perform arithmetic operations, e.g.
%assign wageCost = Top.Employee.PayRate * Top.Employee.Overhead
"%assign wageCost = Top.Employee.PayRate * Top.Employee.Overhead"
wageCost expands to Top.Employee.PayRate * Top.Employee.Overhead ...
<- %<Top.Employee.PayRate> * %<Top.Employee.Overhead> = %<wageCost>
•第1行——回声输出。
•第2行--%赋值语句,用于计算TLC存储在局部变量wageCost中的值。
•第3行——与第2行的操作相呼应。

•第4行和第5行——编写一个语句。省略号(例如,键入三个连续的句点…)表示语句在下一行继续,但如果语句有输出,TLC不会插入换行符。要继续语句并插入换行符,请将省略号替换为反斜杠(\)。

1.3.6 修改记录

一旦读入内存,您就可以修改和操纵记录,就像您通过赋值创建的变量一样。read-guide.tlc的下一段将替换记录字段 Top.Employee.GrossRate的值::

* Put variables into a field, e.g.
%assign Top.Employee.GrossRate = wageCost
"%assign Top.Employee.GrossRate = wageCost"
Top.Employee.GrossRate expands to wageCost = %<Top.Employee.GrossRate>
对记录的这种更改是不持久的(因为记录文件是TLC的输入;其他文件类型,如C源代码,是输出),但可能很有用

除了%assign之外,您还可以使用几个TLC指令来修改记录:

%createrecord 创建新的顶级记录,并可能在其中指定子记录,包括名称/值对。
%addtorecord 将字段添加到现有记录中。新字段可以是名称/值对,也可以是现有记录的别名。
%mergerecord 合并一个或多个记录。第一条记录包含自身以及所有其他记录的副本
命令指定的内容,按顺序排列。

%copyrecord   与%createrecord一样创建新记录,但记录的组件来自您指定的现有记录。
%undef var   从作用域中移除(删除)var(变量或记录)。如果var是记录中的字段,TLC会从记录中删除该字段。如果var是一个记录数组(列表),TLC会删除数组的第一个元素;其余元件仍然可访问。您只能删除使用%createrecord或%copyrecord创建的记录。

1.3.7索引列表

记录文件可以包含具有相同标识符的列表或记录序列。我们的示例包含一个由三条记录组成的列表,这些记录在Top范围内被标识为Project。列表引用按照它们在记录文件中出现的顺序进行索引,从0开始编号。以下是从项目列表的名称字段编译数据的TLC代码:

* Index lists of values, e.g.
%assign projects = Top.Project[0].Name + ", " + Top.Project[1].Name...
+ ", " + Top.Project[2].Name
"%assign projects = Top.Project[0].Name + ", " + Top.Project[1].Name..."
"+ ", " + Top.Project[2].Name"
projects expands to Top.Project[0].Name + ", " + Top.Project[1].Name
+ ", " + Top.Project[2].Name = %<projects>
Scope.Record[n].Field 字段语法类似于C中用于引用结构数组中元素的语法。
虽然如上所述的显式索引是完全可以接受的,但在遍历整个列表时,通常最好使用循环构造,如下文所示。

1.3.8循环遍历列表
按照惯例,列表占用的记录文件部分前面有一条记录,指示存在多少列表元素。在model.rtw文件中,这些参数被声明为NumIdent,其中Ident是用于以下列表中记录的标识符。在guide.rtw中,项目列表如下:

    NumProject 3 # Indicates length of following list

    Project { # First list item, called Top.Project[0]
        Name "Tea" # Alpha field Name, Top.Project[0].Name
        Difficulty 3 # Numeric field Top.Project[0].Difficulty
    } # End of first list item

    Project { # Second list item, called Top.Project[1]
        Name "Gillian" # Alpha field Name, Top.Project[1].Name
        Difficulty 8 # Numeric field Top.Project[1].Difficulty
    } # End of second list item

    Project { # Third list item, called Top.Project[2]
        Name "Zaphod" # Alpha field Name, Top.Project[2].Name
        Difficulty 10 # Numeric field Top.Project[2].Difficulty
    } # End of third list item

因此,NumProject的值描述了项目记录的数量。

注意,model.rtw文件也可能包含以Num开头但不是列表大小参数的记录。TLC不要求列表大小参数以Num开头。因此,在解释NumIdent记录标识符时需要谨慎。内置的TLC函数SIZE()可以确定指定范围内的记录数量,从而确定列表的长度。

read-guide.tlc的最后一段使用由NumProject参数控制的%foreach循环来迭代Project列表并操纵其值:

* Traverse and manipulate list data via loops, e.g.
%assign diffSum = 0.0
%foreach i = Top.NumProject
- At top of Loop, Project = %<Top.Project[i].Name>; Difficulty =...
%<Top.Project[i].Difficulty>
%assign diffSum = diffSum + Top.Project[i].Difficulty

* Traverse and manipulate list data via loops, e.g.
%assign diffSum = 0.0
%foreach i = Top.NumProject
- At top of Loop, Project = %<Top.Project[i].Name>; Difficulty =...
%<Top.Project[i].Difficulty>
%assign diffSum = diffSum + Top.Project[i].Difficulty

您可能还记得,TLC输出如下:

* Traverse and manipulate list data via loops, e.g.
%assign diffSum = 0.0
%foreach i = Top.NumProject
- At top of Loop, Project = %<Top.Project[i].Name>; Difficulty =...
%<Top.Project[i].Difficulty>
%assign diffSum = diffSum + Top.Project[i].Difficulty

* Traverse and manipulate list data via loops, e.g.
%assign diffSum = 0.0
%foreach i = Top.NumProject
- At top of Loop, Project = %<Top.Project[i].Name>; Difficulty =...
%<Top.Project[i].Difficulty>
%assign diffSum = diffSum + Top.Project[i].Difficulty

初始化求和变量diffSum后,进入%foreach循环,变量i声明为循环计数器,迭代到NumProject。
循环的范围是遇到的所有语句,直到相应的
%已达到endforeach(%foreach循环可能嵌套)。

注意:循环迭代隐式地从零开始,范围比指定上限的索引小一个。循环索引是循环体的局部索引

1.4 修改read-guild.tlc

现在您已经学习了read-guide.tlc,是时候对其进行修改了。本练习介绍了两个重要的tlc工具,文件控制和范围控制。您可以在read-guide.tlc脚本中实现这两个。

1.4.1文件控制基础

TLC脚本几乎总是以字符流的形式产生输出。输出通常被定向到一个或多个缓冲区和文件,统称为流。到目前为止,您已经将read-guide.tlc的输出定向到MATLAB命令窗口,因为您在命令行中包含了-v开关。通过在运行read-guide.tlc时省略-v来证明这一点。在命令行中输入:

tlc -r guide.rtw read-guide.tlc
 

似乎什么也没发生。事实上,脚本已经执行,但所有输出都指向一个空设备(有时称为“位桶”)。
总是有一个活动的输出文件,即使它为空。要指定、打开和关闭文件,请使用以下TLC指令:

%openfile streamid ="filename" , "mode"
%closefile streamid
%selectfile streamid

如果不提供文件名,后续输出将流向以streamid命名的内存缓冲区。如果不指定模式,TLC将打开文件进行写入并删除任何现有内容(受系统级文件保护机制的约束)。有效的模式标识符是a(append)和w(write,默认值)。
将这些字符括在引号中。
%openfile指令创建一个文件/缓冲区(在w模式下),或打开一个现有的文件/缓冲区时(在a模式下)。注意文件规范所需的等号。可以打开任意数量的流进行写入,但一次只能有一个流处于活动状态。要切换输出流,请使用%selectfile指令。在处理完文件之前,您不需要关闭它们。
默认输出流为NULL,您可以使用流ID NULL_FILE重新指定。另一个内置流是STDOUT。当使用激活时
%选择文件,STDOUT将输出定向到MATLAB命令窗口。

注意:流NULL_FILE和STDOUT始终处于打开状态。使用%openfile指定它们会产生错误。使用%selectfile激活它们。

指令%closefile关闭当前输出文件或缓冲区。直到遇到openfile或%selectfile指令时,输出将转到之前打开的流(或者,如果不存在,则为null)。使用%selectfile指定要读取或写入的开放流。在实践中,许多TLC脚本将输出数据写入单独的缓冲区,然后按顺序选择缓冲区,并将其内容假脱机到一个或多个文件中。

1.4.2实施输出文件控制

在tlctutorial/guide文件夹中,找到read-guide-file-src.tlc文件。
此文件的提供版本包含注释和添加的三行文本。编辑此文件以实现输出文件控制,如下所示:

  • 1在文本编辑器中打开read-guide-file-src.tlc。
  • 2将文件另存为read-guide-file.tlc。
  • 3注意以%%->开头的五行注释。
  • 在每条注释下,插入所示的TLC指令。
  • 4将编辑后的文件另存为read-guide-file.tlc。
  • 5使用以下命令执行read-guide-file.tlc:
  • tlc -r guide.rtw read-guide-file.tlc
    如果成功,TLC将创建包含预期输出的guidetext.txt文件,并显示MATLAB命令窗口

*** Output being directed to file: guidetext.txt
*** We're almost done . . .
*** Processing completed.
 

如果您没有看到这些消息,或者没有生成文本文件,请查看材料并重试。如果问题仍然存在,请检查guide/solutions子文件夹中的read-guide-file.tlc,了解应如何指定文件控制。

1.4.3 作用域基础

“学习记录文件的结构”解释了记录的层次结构。每条记录都存在于由其嵌套的记录定义的范围内。示例文件guide.rtw包含以下范围:

Top
Top.Employee
Top.Project[0]
Top.Project[1]
Top.Project[2]
要引用字段或记录,通常需要指定其范围,即使没有其他包含标识符的上下文。例如,在guide.rtw中,字段FirstName仅存在于作用域Top.Employee中你必须把它称为top.Employee.FirstName。
当模型呈现深度嵌套的作用域时,这可能会导致非常长的标识符,这些标识符既乏味又容易输入错误。例如:

CompiledModel.BlockOutputs.BlockOutput.ReusedBlockOutput

此标识符的作用域很长,并且具有类似的项目名称,您很容易输入错误。

%with/%endwith指令减轻了正确编码TLC脚本的负担,并阐明了它们的控制流程。语法是

%with RecordName
        TLC statements]
%endwith
每个%with后面都有一个%endwith,这些对可能是嵌套的(但不重叠)。如果RecordName低于顶级,则无需在其描述中包含顶级作用域。例如,将guide.rtw的当前范围设置为guide.rtwTop.Employee,具体代码是:

%with Employee
        [TLC statements]
%endwith

当然,%with Top.Employee 也是有效的语法。一旦被括号括起来%使用/%endwith,TLC语句中的记录标识符不再需要您指定其外部作用域。但是,请注意以下情况:

•您可以访问当前%范围之外的记录,但必须完全限定它们(例如,使用记录名称和字段)。
•每当你对带指令的%以内的记录进行分配时,你必须完全限定它们。

1.4.4 使用%with修改作用域

在本练习的最后一段中,您将通过添加以下内容来修改TLC脚本
%带有/%endwith指令。您还需要编辑记录标识符名称(但不是局部变量的名称),以考虑由%with指令引起的范围变化。
1在文本编辑器中打开TLC脚本read-guide-scope-src.TLC。
2将文件另存为read-guide-scope.tlc。
3注意以%%->开头的注释行。
在每条评论下,插入TLC指令或修改已有的声明,如所示。
4将编辑后的文件另存为read-guide-scope.tlc。
5使用以下命令执行read-guide-scope.tlc:

tlc -v -r guide.rtw read-guide-scope.tlc
输出应该与read-guide.tlc中的输出完全相同,除了可能存在空白,您可能通过缩进%with/%endwith内的代码段或消除空白行来引入空白。
使用上下文在%内完全指定作用域并不是错误,只是没有必要。但是,在将其分配给记录时未能完全指定其范围(例如,%assign-GrossRate=wageCost)是无效的。
如果运行脚本时出现错误,请查看上面关于范围的讨论,并编辑read-guide-scope.tlc以消除这些错误。作为最后的手段,请检查/solutions子文件夹中的read-guide-scope.tlc,看看您应该如何在本练习中处理范围。
有关更多信息,请参阅“在model.rtw文件中使用作用域”和“变量作用域”。

1.5 传递和使用参数

您可以使用TLC命令和内置函数将参数从命令行传递到正在执行的TLC文件。最通用的命令开关是

-a,它分配任意变量。例如:

tlc -r input.rtw -avar=1 -afoo= abc any.tlc
通过-a传递这对字符串的结果与在正在执行的文件(这里是any.tlc)中声明和初始化局部变量的结果相同。例如:

%assign var = 1
%assign foo = abc
您不需要在TLC文件中声明这些变量,当使用-a设置时,它们可以使用。但是,如果代码在调用文件时分配了您没有使用-a开关指定的未声明变量,则会导致错误。另请注意(与-r开关相反)不应分隔任何空格
-a来自您声明的参数。
在本教程的最后一节中,您将使用内置函数GET_COMMAND_SWITCH()打印TLC脚本中使用的记录文件的名称,并提供一个参数来控制是否抑制代码。默认情况下,代码会被执行,但如果命令行包含-allist=0,则代码会被抑制:

  • 1在文本编辑器中打开TLC脚本read-guide-param-src.TLC。
  • 2将文件另存为read-guide-param.tlc。
  • 3要使程序能够从命令行访问输入文件名,请执行以下操作:

           1)在%selectfile STDOUT行下方添加以下行:

%assign inputfile = GET_COMMAND_SWITCH ("r")
%assign指令声明并设置变量。在这种情况下,它包含一个字符串文件名标识符。GET_COMMAND_SWITCH()返回指定TLC命令开关后的任何字符串参数。内置函数名必须始终使用大写字母。

        2)将“*** WORKING WITH RECORDFILE”更改为如下内容:

        *** WORKING WITH RECORDFILE %<inputfile>

  • 4要控制是否执行一段TLC代码,请执行以下操作:

1)“%assign inputfile=GET_COMMAND_SWITCH(“r”)”行下方添加:

%if (!EXISTS(list))
        %assign list = 1
%endif
程序通过内置函数EXISTS()检查是否已声明列表参数。如果不存在列表变量,程序会分配一个。此代码确保列表已定义,默认情况下其值为TRUE。

2)将代码行括在%if块内。

%if (list)
        *  Assign contents of a field to a variable, e.g.
        %assign worker = FirstName
        "%assign worker = FirstName"
        worker expands to FirstName = %<worker>
%endif

现在,只有当list为TRUE时,分配worker的代码才会发送到输出。

3)保存文件read-guide-param.tlc.

  • 5.执行read-guide-param.tlc.文件,并检查输出,使用如下指令:

tlc -r guide.rtw read-guide-param.tlc
结果如下:

*** WORKING WITH RECORDFILE [guide.rtw]
* Assign contents of a field to a variable, e.g.
"%assign worker = FirstName"
worker expands to FirstName = Arthur
***END

  • 6.使用命令执行read-guide-param.tlc

tlc-r guide.rtw-allist=0 read-guide-param.tlc

使用-allist=0开关,输出仅显示if语句之外的信息。

*** WORKING WITH RECORDFILE [guide.rtw]
***END


1.6 总结

前面的练习检查了记录文件的结构,并扩展了如何使用TLC指令。以下TLC指令通常用于TLC脚本(有关详细说明,请参阅第6章“指令和内置函数”):

%addincludepath 启用TLC查找包含的文件。
%addtorecord    将字段添加到现有记录中。新字段可以是名称/值对,也可以是现有记录的别名。
%assign 分配创建或修改变量。
%copyrecord  创建新记录,如果合适,在其中指定子记录,包括名称/值对。记录的组成部分来自指定的现有记录。
%createrecord  创建新的顶级记录,并在适当的情况下指定其中的子记录,包括名称/值对。
%foreach/%endforeach  迭代循环变量从0到上限。
%if/%endif   控制是否执行代码,如C中所示。
%include  将一个文件插入另一个文件,如C中所示。
%mergerecord合并记录合并一个或多个记录。第一条记录按顺序包含其自身以及命令指定的所有其他记录内容的副本。

%selectfile 直接输出到流或文件。
%undef var 

从作用域中移除(删除)var(变量或记录)。如果var是记录中的字段,TLC会从记录中删除该字段。如果var是一个记录数组(列表),TLC会删除数组的第一个元素;其余元件仍然可访问。只能删除通过%createrecord或%copyrecord创建的记录

%with/%endwith 添加范围以简化引用块

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值