查找规则¶
回想我们在讨论组织内容时的以下例子:
within ModelicaByExample.PackageExamples;
package NestedPackages
"An example of how packages can be used to organize things"
package Types
type Rabbits = Real(quantity="Rabbits", min=0);
type Wolves = Real(quantity="Wolves", min=0);
type RabbitReproduction = Real(quantity="Rabbit Reproduction", min=0);
type RabbitFatalities = Real(quantity="Rabbit Fatalities", min=0);
type WolfReproduction = Real(quantity="Wolf Reproduction", min=0);
type WolfFatalities = Real(quantity="Wolf Fatalities", min=0);
end Types;
model LotkaVolterra "Lotka-Volterra with types"
parameter Types.RabbitReproduction alpha=0.1;
parameter Types.RabbitFatalities beta=0.02;
parameter Types.WolfReproduction gamma=0.4;
parameter Types.WolfFatalities delta=0.02;
parameter Types.Rabbits x0=10;
parameter Types.Wolves y0=10;
Types.Rabbits x(start=x0);
Types.Wolves y(start=y0);
equation
der(x) = x*(alpha-beta*y);
der(y) = -y*(gamma-delta*x);
end LotkaVolterra;
end NestedPackages;
在我们讨论引用包内内容时,演示例子中所有引用了的类型均使用了全限定名。但上述例子则不然。我们从LotkaVolterra
模型中可以看到,Wolves
类型被引用为:
parameter Types.Wolves y0=10;
而不是:
parameter ModelicaByExample.PackageExamples.NestedPackages.Types.Wolves y0=10;
换言之,我们没有使用全限定名。但LotkaVolterra
模型的编译毫无问题。那么Modelica编译器是如何知道要使用哪个Wolves
的定义呢?
答案涉及到Modelica的“名称查找”。Modelica的名称查找涉及指定定义的搜索。Modelica的类型名称一般为限定(虽然不一定为完全限定)名称。这意味着名称中可能包含了.
。例如Modelica.SIunits.Voltage
。为了找到与一个名称相匹配的定义,Modelica的编译器开始由限定名称中寻找第一项名称,如Modelica
。编译器按以下顺序搜索匹配的定义:
在内建类型中寻找匹配的名称
在当前定义(包括了其继承的定义)内寻找j的具有匹配名称的内嵌定义
查找在当前定义内名称匹配的导入定义(不包括继承的导入定义)
在当前定义的父包查找与名称匹配的内嵌定义(包括继承的定义)
查找在当前定义的父包内名称匹配的导入定义(不包括继承的导入定义)
(用上述算法)上溯每个父包直到下列条件至少有一个成立:
某个父包有
encapsulated
限定词,在这种情况下搜索终止。没有更多的父包,在这种情况下会在根级别的包内搜索匹配。
如果编译器在搜索所有这些地点后不能发现给定名称,那么搜索失败,类型无法找到。如果搜索成功,那么我们便找到在限定名定义里的首个名称。如果名称是非限定名称(即名称内不包含.
),那么我们就完成任务了。然而,如果名称中有其他组件,那么该组件必须包含在由前述搜索返回的定义范围内,亦即前述定义内的内嵌定义。如果编译器在相应的内嵌定义里不能找到限定名称内的任一组件,那么搜索失败,类型无法找到。
第一眼看上去这很复杂。然而大多数情况下,这些规则其实不太重要。原因是,正如我们前面所讨论的一样,大多数的图形化Modelica环境会使用全限定名称。Modelica代码里大部分类型的名称要么引用本地定义或是用全限定名称来指定。
重复命名
读者一定要避免在嵌套包里使用和顶级包相同的名称。原因是,基于查找规则,搜索会上溯包的层次结构,从而会造成一个问题。其结果是,编译器会先发现嵌套定义,而不是根级别的定义。这意味着基于全限定名称(相对于包根目录引用的名称)的查找将会失败,因为编译器将首先找到嵌套定义。