Переменные
Окно переменных — это инструмент Engee, предназначенный для управления переменными. В нем содержится информация о переменных, созданных в процессе работы.
Переменная — это имя, ассоциированное (связанное) с некоторым значением. Значение — это число, строка, матрица или другой тип данных. Переменные в Engee доступны в командной строке, редакторе скриптов и окне Переменные. Переменные также могут быть переданы как параметры блоков среды моделирования.
Для открытия окна переменных выберете значок в левом верхнем меню рабочей области Engee. Переменные можно создать:
-
Через командную строку:
-
Через редактор скриптов. Для этого создайте новый скрипт (в примере назван new), добавьте область ввода кода и создайте переменные, нажав на зеленую кнопку Выполнить .
В окне переменных представлены следующие столбцы:
-
имя переменной;
-
значение переменной;
-
тип данных переменной;
-
размер переменной (в килобайтах).
Можно переместить столбцы окна переменных, перетаскивая заголовок мышью. Также можно менять ширину этих столбцов.
Можно редактировать значение переменной, нажав по имени двойным кликом или вызвав контекстное меню правой кнопкой мыши для выбора опции Редактировать значение.
Для редактирования имени переменной выберите соответствующую опцию из контекстного меню, вызванного правым кликом по имени переменной, либо дважды нажав по ее имени:
Подробнее о разрешенных именах переменных можно ознакомиться в статье. |
Можно скопировать имя или значение переменной, нажав по имени и выбрав функцию Копировать в контекстном меню.
Можно очистить ненужные переменные, нажав правой кнопкой мыши и выбрав Очистить (также используется клавиша delete). Для очистки всех переменных выберите иконку метлы в правом верхнем углу.
В языке программирования Julia можно использовать не только латинские символы, но и символы из Unicode, например, \delta . При вводе символа \delta в командной строке, нажатии клавиши Tab можно получить соответствующий символ, который можно использовать в качестве имени переменной. Полученный символ можно скопировать из командной строки и вставить в редактор скриптов для использования.
|
Содержимое переменных
Значение переменной с типом данных Vector или Matrix может быть изменено в редакторе векторов и матриц в окне переменных.
Для примера поставьте любое число в любую свободную ячейку матрицы — матрица будет автоматически расширена (заполнена нулями) до этой ячейки. Ввод переменной в командной строке или редакторе скрипта покажет измененную матрицу:
Добавление строки с текстом в ячейку матрицы изменит ее тип данных на Any, что позволит переменной содержать значения любого типа. Вы можете изменять матрицу в любое время в редакторе векторов и матриц. |
Импорт и экспорт
Engee поддерживает запись и выгрузку переменных в форматах .mat и .jld2:
-
.mat — формат, который используется в программном обеспечении MATLAB.
-
.jld2 — формат, который используется в языке программирования Julia.
Чтобы записать переменные в .mat или .jld формат, выберите их в окне переменных и правой кнопкой мыши вызовите контекстное меню. В меню нажмите Сохранить как…:
Это откроет окно экспорта переменных:
-
Задайте имя файла, который будет содержать переменные.
-
Выберите формат экспортируемого файла — MAT или JLD2.
-
Отследите директорию, в которую будет сохранен файл.
-
Задайте путь, по которому будет сохранен файл. В этой области будут отображены все папки файлового браузера Engee.
-
Возвращайтесь из текущей директории на уровни выше, вплоть до
/user
(начала пути файлового браузера Engee). -
Создайте новую папку в директории пункта 3.
Сохраненный файл в формате MAT или JLD2 отобразится в окне файлов . Двойной клик левой кнопкой мыши по сохраненному файлу импортирует переменные в рабочую область Engee. Импортированные переменные автоматически добавятся в окно переменных .
MAT
Используйте пакет MAT.jl для работы с данными в формате .mat в Julia (подробнее здесь). |
Вы можете работать с форматом MAT программно, через редактор скриптов или командную строку. Например, рассмотрим следующий код чтения одной переменной из MAT:
using MAT # подключаем библиотеку MAT для работы с файлами .mat
a = 1 # задаем переменную а со значением 1
file = matopen("/user/mat_file.mat") # открываем файл mat_file.mat для чтения с помощью функции matopen и создаем объект file
variable_mat = read(file, "a") # считывает переменную "a" из файла типа MAT и присваивает ее значение переменной variable_mat
close(file) # закрывает файл
Для записи переменной в MAT-файл используем код:
variable_1 = 1 # зададим переменную variable_1 и присвоим ей значение 1
file = matopen("/user/new_mat_file.mat", "w") # открывает файл new_mat_file.mat в режиме записи ("w")
write(file, "variable_mat", variable_1) # записывает значение переменной variable_1 в переменную variable_mat в файл new_mat_file.mat
close(file) # закрывает файл
С помощью кода мы записали переменную в файл формата MAT. Схожим образом, через функцию matwrite, можем записать Dict (словарь) в формат MAT, используя его ключи в качестве имен переменных:
mat_Dict = Dict(
"variable_1" => 1,
"variable_2" => -2,
"variable_3" => "Hello, world")
matwrite("/user/Dict.mat", mat_Dict)
Код создаст файл Dict.mat, содержащий словарь с именами переменных.
Вы можете использовать аргумент сжатия данных
|
JLD2
Используйте пакет JLD2.jl для работы с данными в формате .jld2 в Julia (подробнее здесь). |
Загрузим модуль JLD2:
import Pkg; #импорт менеджера пакетов
Pkg.add("JLD2") #добавления пакета JLD2
Пакет JLD2.jl поддерживает интерфейсы функции на подобии jldsave
. Например:
using JLD2 #загрузка модуля
x = 1
y = 2
z = 42
# Простейший случай:
jldsave("example.jld2"; x, y, z)
# это равносильно
jldsave("example.jld2"; x=x, y=y, z=z)
# Новые имена можно присваивать лишь части аргументов
jldsave("example.jld2"; x, a=y, z)
# а чтобы внести полную путаницу, можно сделать так
jldsave("example.jld2"; z=x, x=y, y=z)
Если в файле требуется сохранить только один объект для последующей загрузки, можно воспользоваться функциями save_object
и load_object
. Например:
save_object(filename, x) #сохраняет объект x в новом JLD2-файле в filename, если файл существует по этому пути, он будет перезаписан.
load_object(filename, x) #загружает объект x
Переменные в Среде вычислений
Чтобы создать новую переменную, введите имя (название переменной) в командную строку/редактор скриптов и оператором присваивания = (знак равно) присвойте нужное значение.
Переменные в 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).
Попытка присвоить переменной несоответствующий тип данных приводит к ошибке и созданию переменной err в окне переменных. Переменная err хранит информацию о последней ошибке в столбце значений в окне переменных. При необходимости скройте переменную err по аналогии с другим переменными или вручную с помощью объекта nothing:
|
Рассмотрим неявную типизацию, в которой программа определяет тип данных переменной на основе содержимого:
При неявной типизации присвоение другого типа данных переменной не вызывает ошибку. Присвоение типа данных происходит на основе содержимого (значения) переменной.
Подробнее ознакомиться с типизацией можно в статье Типы в Engee.
Кортежи
Кортежи (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 (функция) или |
Область видимости определяет, где в коде вы можете использовать переменную. Так, переменные бывают локальные и глобальные:
-
Локальная (внутренняя) переменная — переменная считается локальной, если она объявлена и определена внутри своего родительского элемента, например функции, объекта, блока кода (в котором определена) и т.д. Локальные переменные видны только внутри той области видимости, в которой определены и недоступны за ее пределами. Рассмотрим локальную переменную на примере:
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 появится в файловом браузере только после ее сохранения. Сохранение модели позволит работать с ней в будущем и применять весь функционал файлового браузера. |
Еще больше возможностей для работы с переменными в среде моделирования представлено в статье Программное управление моделированием.
Переменные как параметры блоков
Вы можете управлять параметрами блоков без программных методов, просто устанавливая отдельные значения параметров как переменные. Рассмотрим пример с моделью, представленной в статье. Модель состоит из блоков 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.