Работа с переменными в 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}
Для создания массивов с размерностью больше трех можно использовать значение 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. -
Float — представляет вещественные числа (с плавающей запятой), например
1.0,−3.14. -
String — представляет текст, заключенный в кавычки, например
"Hello, world!". -
Array — представляет упорядоченный набор элементов, например,
[1, 2, 3].
Рассмотрим явное указание типа данных переменных на следующем примере:

Здесь для переменной z был явно задан тип данных Int64, который содержит только целые числа. Попытка присвоить переменной z вещественное число 3.14 (соответствующее типу данных Float64`) вызвало ошибку (InexactError: Int64).
|
Попытка присвоить переменной несоответствующий тип данных приводит к ошибке и созданию переменной
|
Кортежи
Кортежи (Tuple) в Engee — это упорядоченная и неизменяемая структура данных. Кортежи создаются с помощью круглых скобок ( ) и запятых.
Кортежи описываются с помощью параметризованного типа данных 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!", а не весь кортеж целиком.
-
Именованный кортеж — это особый тип кортежа, похожий на обычный кортеж, но в котором каждый элемент имеет свое имя. Вместо обращения к элементам по индексу как в обычных кортежах, в именованных кортежах также можно обращаться к элементам по их именам. Пример именованного кортежа:
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. -
Строка
"Hello, world!".
Далее значения кортежа параллельно присваиваются трем переменным 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"(род занятий). -
name, age, occupation = info— с помощью параллельного присваивания значения кортежа распаковываются в три переменные:name,ageиoccupation. Теперь переменная 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()
Выполнение кода также создало переменную example-function с типом данных Function в окне переменных Engee.
|
После выполнения функции, локальная переменная x выходит из области видимости, и становится недоступной за пределами функции. Это означает, что переменная существует только в пределах блока, в котором была определена.
Попробуем вызвать переменную вне локальной области с помощью следующего кода:
println(x)

В результате выполнения кода мы получили ошибку, что переменная x не определена. Делаем вывод, что локальная переменная ограничивается областью видимости внутри блока кода (в котором определена) и не определяется извне.
| Внешний код не имеет доступа к локальной переменной и выдает ошибку, что локальная переменная не определена. |
Глобальная (внешняя) переменная — переменная считается глобальной, если определена вне функций, циклов и блоков кода с локальной областью видимости. Глобальная переменная доступна из любой части кода и находится в глобальной области видимости, которая по умолчанию является модулем Main. Внутри модуля Main можно определить подмодули, для которых будут свои глобальные переменные. Рассмотрим глобальную переменную на примере:
global_var = 20
function example_function()
println(global_var)
end
Этот код устанавливает глобальную переменную global_var и определяет функцию example_function(). example_function() выводит значение глобальной переменной. Для вывода результата функции введите следующий код:
example_function()
println(global_var)# можно использовать глобальную переменную за пределами функции
После вызова example_function() выводится значение глобальной переменной global_var равное 20. Функция 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, Get Current Model, получение текущей модели). Полученный объект присваивается переменной m.
Далее заменим параметры моделирования с помощью set_param!:
engee.set_param!(m, "name" => "Tsit5", "type" => "variable-step")
В коде изменяются параметры решателя и его шаг с постоянного на переменный.
Посмотрите, изменились ли параметры модели с помощью метода gcm:
engee.get_param("model")
Параметры модели поменялись на новые благодаря переменной m:

Далее с помощью метода run запустим симуляцию модели через переменную:
engee.run("m")
После успешной симуляции сохраните модель Engee со свойствами объекта (переменная m) по заданному пути в файловый браузер. В нашем случае:
engee.save(m,"/user/start/examples/base_simulation/command_control/model.engee"; force=true)
Если файл с таким именем уже существует (в нашем случае model.engee) — опция force=true перезапишет модель данными из переменной m без запроса подтверждения. Это полезно, если нужно перезаписать существующие файлы при сохранении модели.
Модель Engee появится в файловом браузере только после ее сохранения. Сохранение модели позволит работать с ней в будущем и применять весь функционал файлового браузера .
|
Еще больше возможностей для работы с переменными в среде моделирования представлено в статье Программное управление моделированием.
Переменные как параметры блоков
Вы можете управлять параметрами блоков без программных методов, просто устанавливая отдельные значения параметров как переменные. Рассмотрим пример с моделью, представленной в статье Программная обработка результатов симуляции в Engee. Модель состоит из блоков Sine Wave и Terminator, для сигнала между ними включена запись:

Блок Sine Wave – единственный блок в модели, чьи параметры можно настроить. Нажмите левой кнопкой мыши по блоку и задайте переменные для каждого параметра в соответствии с рисунком:

После установки имен переменных для значений блока присвоим значения для каждой из этих переменных:
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, или вручную, через кнопку Запустить модель
. На графике будет видно, что блок Sine Wave изменил свои параметры и симуляция прошла корректно:

Устанавливать переменные в качестве параметров блоков полезно в случае, если блок имеет зависимости, влияющие на тот или иной параметр:
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, убедимся в этом на графике результатов симуляции:

| Использование разных наборов значений переменных (сохраненных в разных скриптах) позволяет моделировать объекты с одинаковой структурой, но разными параметрами. |
Сохранение результатов симуляции
Можно сохранить результаты симуляции модели в оперативную память Engee с помощью переменной simout. Переменная создается автоматически после завершения симуляции модели при включенной записи сигналов модели. Рассмотрим пример с моделью из предыдущего пункта, но используем созданную ранее переменную 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.

