在 Engee 中使用变量
在 Engee 中,变量可以通过不同的方式进行设置。让我们根据下面的代码和 a
、b
、c
三个变量来考虑主要的赋值方法:
a = 1 # Простое присваивание
a::Float64 = 1.0 # Присваивание с явным указанием типа данных
b = 1 + 2 # Присваивание через вычисление выражения
c = b = 5 # Множественное присваивание
a,b,c = (10,20,30) # Распаковка кортежа
执行代码的结果如下:
-
变量
a
被赋值为1
。这是最简单的变量赋值; -
变量
a
被赋值为1
,但明确指定了 Float64(浮点)数据类型;现在它的实际值是1.0
; -
变量 b 被赋值为表达式
1 + 2
的结果,即3
; -
值
5
分配给变量 b 和 c; -
元组`(10, 20, 30)`中的值被解压缩并分别赋值给变量 a、b 和 c;
请注意,Engee 中的变量可以是标量变量、向量变量和矩阵变量:
-
标量变量 - 只有一个数据值。使用整数、实数、逻辑值和字符串形式的变量来指定它。例如
v = 1 # Целое число, тип данных Int64 v = 1.0 # Вещественное число, тип данных Float64 v = true # Логическое значение, тип данных Bool v = "a" # Символьная строка, тип данных String
-
*矢量*是元素的有序集合,可表示为一维数组。它使用包含整数或实数、逻辑值或字符串的变量来指定。例如
v = [1, 2, 3] # Вектор целых чисел, тип данных Vector{Int64} v = [1.0, 2.0, 3.0] # Вектор вещественных чисел, тип данных Vector{Float64} v = [true, false] # Вектор логических значений, тип данных Vector{Bool} v = ["a", "b", "с"] # Вектор символьных строк, тип данных Vector{String}
-
*矩阵*是由行和列组成的二维数据数组。矩阵可用于同时表示多个变量。矩阵的每个元素都可以是标量值,如整数、实数、逻辑值或字符串。例如
v = [1 2 3] # Матрица целых чисел, тип данных Matrix{Int64} v = [1.0 2.0 3.0] # Матрица вещественных чисел, тип данных Matrix{Float64} v = [true false] # Матрица логических значений, тип данных Matrix{Bool} v = ["a" "b" "с"] # Матрица символьных строк, тип данных Matrix{String} v = ([1 2 3; 4 5 6; 7 8 9]) # Многострочная 矩阵, тип данных Matrix{Int64}
要创建维数大于 3 的数组,可以使用 undef
值,它表示数组未初始化。这种数组的元素可以包含任意数据。让我们从这两种结构中选择一种来创建数组:
Array{T}(undef, dims)
Array{T,N}(undef, dims)
在哪里?
-
T` - 数组数据类型。
-
N
- 数组的维数。可以明确指定,也可以由dims
的长度和数量决定。如果明确指定,则必须与dims
的长度和数量一致。 -
dims
- 整数或多个整数参数,与每个维度的长度相对应。
例如
A = Array{Float64, 3}(undef, 2, 3, 4)
# или
A = Array{Float64}(undef, 2, 3, 4) # Получим трехмерный массив с размерностью 2*3*4, тип данных Float64, размерность задается автоматически
数组创建结果的输出_
2×3×4 Array{Float64, 3}:
[:, :, 1] =
6.94241e-310 6.94267e-310 6.94267e-310
6.94272e-310 6.94267e-310 6.94267e-310
[:, :, 2] =
6.94267e-310 6.94267e-310 6.94267e-310
6.94267e-310 6.94267e-310 6.94267e-310
[:, :, 3] =
6.94267e-310 6.94267e-310 6.94267e-310
6.94267e-310 6.94267e-310 6.94267e-310
[:, :, 4] =
6.94267e-310 6.94267e-310 6.94267e-310
6.94267e-310 6.94267e-310 6.94267e-310
变量类型
*Engee*支持显式和隐式变量类型—这意味着变量的数据类型可以显式指定(强制赋值)或在运行时根据其内容确定。主要数据类型包括
-
Integer - 表示整数,如
1
、100
、-1
。 -
浮点 - 表示实数(浮点),如`1.0`、
-3.14
。 -
String - 表示用倒逗号括起来的文本,如
Hello, world!"
。 -
数组 - 表示元素的有序集合,如`[1, 2, 3]`。
让我们看看下面示例中变量数据类型的显式规范:
这里,为 z
变量明确指定了只包含整数的 Int64 数据类型。如果试图将实数 3.14
(对应于 Float64 数据类型)赋值给 z
变量,则会导致错误(InexactError: Int64)。
试图给变量分配不合适的数据类型会导致错误,并在变量窗口
|
科尔特斯
在 Engee 中,Cortex(元组)是一种有序且不可变的数据结构。元组使用括号 ( )
和逗号创建。
元组使用参数化数据类型 MyType{ParamType1, ParamType2, ParamType3, …}
来描述,并表示直接序列中元素的数据类型。元组中的元素可以有不同的数据类型。例如,一个元组可以同时包含整数、实数和带引号的文本。让我们来看看这样一个元组:
tuple = (1, 3.14, "Hello, world!")
在这种情况下,元组的数据类型为 Tuple(Int64、Float64、String)。
元组可以是普通的,也可以命名:
-
*普通元组*是一种有序且不可变的数据结构。普通元组的每个元素可以是任何数据类型。其元素按特定顺序排列。元素通过索引进行访问。让我们举例说明这样一个元组:
x = (1, 3.14, "Hello, world!") x[3]
在示例中,变量
x
包含三个值--1
、3.14
和`"Hello, world!"`。这些值分别对应序数索引[1]
、[2]
和[3]
。借助包含普通元组值的变量x
和索引[3]
,我们得到了变量的一个特定值--"Hello, World!"
,但不是整个元组。 -
Named tuple 是一种特殊类型的元组,与普通元组类似,但其中每个元素都有自己的名字。命名元组不像普通元组那样通过索引来指代元素,它还可以通过元素的名称来指代元素。命名元组的一个例子
x = (a=1, b=3.14, c="Hello, world!") println(x.a) println(x.b) println(x.c) println(x[1]) println(x[2]) println(x[3])
此示例不仅支持通过索引访问元组元素(如通常的元组 [1],…,[n]
),还支持通过变量名--x.a
, x.b
,`x.c`等—访问元组元素。
由于元组是一种不可变的结构—其元素的顺序很重要,并且会被保留。元组的不变性使您可以处理多个赋值。 |
*多重赋值*是一种允许使用元组同时为多个变量赋值的功能。这一功能可以避免使用临时变量,并提高代码的可读性。例如
tuple = (1, 3.14, "Hello, world!")
a, b, c = tuple
这里创建了一个有三个元素的元组 tuple
:
-
整数
1
。 -
实数
3.14
。 -
字符串 `"你好,世界!"。
然后,元组值将分别并行赋值给三个变量 a
、b
和 c
。代码执行后,a
变量将包含`1`值,b`变量将包含`3.14`值,`c`变量将包含字符串`"Hello, world!"
。通过这种方式,您可以同时为多个变量赋值,而无需使用字符串插值法明确指定元组值:
info = ("Ilya", 25, "Engineer")
name, age, occupation = info
println("Name: $name, Age: $age, Occupation: $occupation")
在这段代码中,元组用于存储信息,并行赋值用于将元组值解压缩到单个变量中。然后使用 $
字面进行字符串插值,以输出人员信息:
-
info = ("Ilya", 25, "Engineer")
- 创建的元组info
包含三个元素:字符串"Ilya"(姓名)、整数 `25
(年龄)和字符串 `"Engineer"(职业)。 -
姓名、年龄、职业 = info` - 使用并行赋值,将元组值解压缩到三个变量中:姓名"、"年龄 "和 "职业"。现在,变量 name 包含值`"Ilya"`,age 包含整数`25`,occupation 包含职业`"Engineer"`。
-
println("Name: $name, Age: $age, Occupation: $occupation")
- 使用字符串插值输出字符串(使用$name
、$age
和$occupation
在字符串中插入变量值)。执行这行代码将输出
Name: Ilya, Age: 25, Occupation: Engineer
这样,代码就创建了一个包含个人相关信息的元组,然后将元组解包为单独的变量,以便于访问数据,并将信息输出到脚本编辑器或命令行。
有关在*Engee*中处理元组的更多信息,请参阅Julia中的函数。有关 Julia 中插值的更多信息,请参阅Julia中的字符串。
范围
我们所说的代码块是指由语法结构 function (函数)或 let … 组成的代码片段。end .在这些代码块中,您可以声明局部变量,这些变量只在代码块中可见,不会与代码其他部分的变量(例如全局变量)发生冲突。
|
*可见性*定义了在代码的哪个部分可以使用变量。因此,变量有局部和全局之分:
-
局部(内部)变量 - 如果变量是在其父元素(如函数、对象、代码块(在其中定义)等)内部声明和定义的,则视为局部变量。局部变量只在其定义的作用域内可见,在作用域外无法访问。让我们以局部变量为例进行说明:
function example_function() x = 10 println(x) end
这里的 function example_function()
定义了一个名为 example_function
的函数,它在局部作用域中包含局部变量 x
。
使用 example_function()
命令进一步调用该函数,将输出 x
变量的值等于 10
:
example_function()
执行代码的同时,还在 Engee 变量窗口中创建了一个数据类型为 Function 的 example-function 变量。
|
函数执行后,局部变量 x
将退出作用域,在函数之外无法访问。这意味着该变量只存在于定义它的代码块中。
让我们尝试使用下面的代码在本地作用域之外调用该变量:
println(x)
代码执行的结果是,我们得到了一个错误,即 x
变量未定义。我们得出结论:局部变量仅限于代码块(在代码块中定义)内部的作用域,不能从外部定义。
外部代码无法访问局部变量,并产生了局部变量未定义的错误。 |
全局(外部)变量 - 如果变量定义在函数、循环和具有局部作用域的代码块之外,则该变量被视为全局变量。 全局变量可从代码的任何部分访问,并位于全局作用域(默认情况下为 Main 模块)中。在主模块中,你可以定义子模块,这些子模块将拥有自己的全局变量。让我们以全局变量为例进行说明:
global_var = 20
function example_function()
println(global_var)
end
这段代码设置了全局变量 global_var
并定义了函数 example_function()
。example_function()
输出全局变量的值。要输出函数的结果,请输入以下代码:
example_function()
println(global_var)# можно использовать глобальную переменную за пределами функции
调用 example_function()
后,将打印等于 20
的全局变量 global_var
的值。println 函数将同时打印变量的两个值,一次来自函数,一次在函数之外。
使用这样的变量可能会很困难,因为当本地作用域中有同名变量时,会出现阴影问题。 |
*变量阴影*是指在特定作用域(例如函数或代码块内部)中使用的同名变量与全局作用域中的变量相同。因此,作用域中的变量会 "阴影"(覆盖)外部作用域中的同名变量。
设置同名变量时要小心—由于阴影作用,受限作用域中的变量可能与程序其他部分中的同名变量具有不同的值或类型。 |
例如,我们来看看创建两个具有相同名称和值且不带阴影的变量的代码:
global_var = 10 # создание глобальной переменной
function example_function()
global_var = 20 # создание локальной переменной (внутри функции) с тем же именем, что и глобальная
println(global_var) # будет использована локальная переменная внутри функции
end
example_function() # вызов функции
println(global_var) # глобальная переменная не была изменена внутри функции, поэтому выводится ее значение
在函数内部创建一个与全局变量同名的局部变量 global_var
。这不会改变全局变量,但会创建一个仅在函数内部可见的新局部变量。这样,两个变量的值将分别输出为 20
(全局变量)和 10
(局部变量)。因此,在不同作用域中创建变量有助于避免重写同名变量的问题。
建模环境中的变量
作为模型参数的变量
在*Engee*中,变量可以指定为模型参数。请看下面的例子:
engee.create("model")
使用create 方法在 Engee 中创建一个名为 model 的模型。接下来,让我们分配一个变量:
m = engee.gcm()
我们使用gcm 方法访问已创建的模型,并返回一个代表该模型的对象(在我们的例子中为 GCM,获取当前模型)。接收到的对象将分配给 m
变量。
接下来,让我们使用set_param! 替换建模参数:
engee.set_param!(m, "name" => "Tsit5", "type" => "variable-step")
代码将求解器参数及其步长从常量改为变量。
使用gcm 方法查看模型参数是否发生变化:
engee.get_param("model")
由于 m
变量,模型参数已变为新参数:
接下来,让我们使用run 方法,通过变量运行模型模拟:
engee.run("m")
模拟成功后,将带有对象属性(变量 m
)的模型 Engee 保存到文件浏览器中的指定路径。在我们的例子中
engee.save(m,"/user/start/examples/base_simulation/command_control/model.engee"; force=true)
如果已经存在具有此名称的文件(在我们的例子中是 model.engee)--force=true
选项将使用 m
变量中的数据覆盖模型,而无需请求确认。如果在保存模型时需要覆盖现有文件,这个选项非常有用。
只有在保存模型时,Engee 模型才会出现在文件浏览器中。保存模型将允许您在将来使用它,并使用文件浏览器![]() |
关于在建模环境中使用变量的更多可能性,请参阅软件控制建模 一文。
变量作为块参数
只需将单个参数值设置为变量,就可以在不使用编程方法的情况下控制块参数。让我们以文章软件处理 Engee 中的模拟结果 中的模型为例进行说明。该模型由区块*正弦波* 和*终结者* 组成,区块间的信号写入功能已启用:
区块*正弦波* 是模型中唯一一个参数可以自定义的区块。左键单击图块,根据图示设置每个参数的变量:
为块值设置变量名后,为每个变量赋值:
a_var = 1 # Amplitude становится равным 1
b_var = 2 # Bias становится равным 2
f_var = 3 # Frequency становится равным 3
p_var = 1 # Phase становится равным 1
s_var = 1 # Sample time становится равным 1
让我们通过将模型名称设置为一个单独的变量(文章前面的 m
)并使用 engee.run
方法,或通过 运行模型 按钮手动运行模型的仿真。图表将显示数据块*正弦波* 的参数发生了变化,仿真运行正常:
如果程序块具有影响一个或另一个参数的依赖关系,将变量设置为程序块参数将非常有用:
a_var = (2*f_var) # Amplitude становится равным 6
b_var = (6/f_var) # Bias становится равным 2
f_var = 3 # Frequency равен 3
p_var = (1 + f_var) # Phase становится равным 4
s_var = 1 # Sample time равен 1
代数表达式,包括包含变量的代数表达式,都可以用作程序块参数:
输出
变量 a_var
为 6
,那么乘以 3
后的振幅将为 18
,我们将通过绘制模拟结果来验证这一点:
使用不同的变量值集(存储在不同的脚本中)可以为结构相同但参数不同的对象建模。 |
保存模拟结果
可以使用 simout
变量将模型模拟结果保存到 Engee RAM 中。该变量在仿真结束并启用模型信号记录时自动创建。让我们以前面的模型为例,使用之前创建的变量 m
。
模型仿真完成后,存储仿真结果的 simout
变量将出现在文件浏览器中。让我们把 simout
中的数据卸载到 m
变量中:
m = collect(simout["newmodel_1/Sine Wave.1"])
使用 Plots 库和我们的变量将模拟结果可视化:
using Plots # подключаем модуль Plots
plot(m.time, m.value) # вывод time (время) и value (значение) из переменной m
代码完成后,我们将看到以下图表:
有关保存仿真结果过程的信息,请参阅软件处理 Engee 中的模拟结果 一文。