Документация Engee
Notebook

Компиляция строк

Открыть пример в Engee

В данном примере рассмотрены альтернативные подходы к заданию действий и переменных с использованием строк. Мы детально рассмотрим функции parse, Meta.parse и eval, а также продемонстрируем их практическое применение.

В ходе работы будут распарсены два файла различных форматов: CSV и TXT, что наглядно покажет возможности этих инструментов для обработки данных и динамического выполнения кода.

Функция parse в Julia используется для преобразования строкового представления данных в соответствующий числовой или другой базовый тип. Она особенно полезна, когда необходимо работать с данными, представленными в виде строк, которые нужно конвертировать, например, в числа, символы или значения других типов.

Например, если у вас есть строка, содержащая число, такое, как "5". С помощью parse можно преобразовать её в целое число. Аналогично строку с плавающей точкой "1.23" можно преобразовать в число с плавающей запятой. Эта функция поддерживает указание типа, к которому должна быть выполнена конвертация.

Примеры использования:

In [ ]:
num1 = parse(Int, "5")   
num2 = parse(Float64, "1.23")  
print("Результаты: $([num1, num2])")
Результаты: [5.0, 1.23]

Функция Meta.parse в Julia используется для преобразования строки, содержащей выражение или вызов функции, в объект типа Expr (выражение Julia). Это мощный инструмент для метапрограммирования, позволяющий анализировать и модифицировать код программы в виде абстрактного синтаксического дерева (AST).

Если в строке содержится выражение, например математическая операция или вызов функции, Meta.parse преобразует его в структурированное представление, которое можно использовать для анализа или выполнения.

Важное отличие от parse:

  1. parse преобразует строки только в конечные значения заданного типа, такие как числа, логические значения или символы. Оно не поддерживает сложные выражения или вызовы функций.
  2. Meta.parse работает исключительно с кодом, представленным в виде строки, и преобразует его в объект Expr, который можно анализировать или передать для выполнения. Пример использования:
In [ ]:
cmd = Meta.parse.("1+1")
Out[0]:
:(1 + 1)

Функция eval в Julia используется для выполнения выражений, представленных в виде объектов типа Expr (абстрактное синтаксическое дерево) или других валидных выражений. Она позволяет динамически запускать код, что делает её мощным инструментом для задач метапрограммирования, но требует осторожного использования из-за потенциальных рисков выполнения нежелательного кода.

Основной принцип работы: eval принимает выражение, анализирует его и выполняет в глобальной области видимости. Это означает, что все переменные и функции, используемые внутри выражения, должны быть определены в глобальной области.

Пример использования:

In [ ]:
eval(cmd)
Out[0]:
2

Далее рассмотрим пример с CSV-файлом. Для этого подгрузим дополнительные библиотеки.

In [ ]:
Pkg.add("CSV")
using DataFrames, CSV
   Resolving package versions...
  No Changes to `~/.project/Project.toml`
  No Changes to `~/.project/Manifest.toml`

Выполним чтение CSV.

In [ ]:
DataFrameCSV = CSV.read("$(@__DIR__)/$("data.csv")", DataFrame)
Out[0]:
9×1 DataFrame
RowTime_and_Ampl
String31
1-1.08,-96.6667
2-1.0799,-126.667
3-1.0798,-143.333
4-1.0797,-166.667
5-1.0796,-176.667
6-1.0795,-166.667
7-1.0794,-146.667
8-1.0793,-126.667
9-1.0792,-96.6667

Выделим из полученного DataFrame столбец с данными.

In [ ]:
Data = DataFrameCSV.Time_and_Ampl
Out[0]:
9-element Vector{String31}:
 "-1.08,-96.6667"
 "-1.0799,-126.667"
 "-1.0798,-143.333"
 "-1.0797,-166.667"
 "-1.0796,-176.667"
 "-1.0795,-166.667"
 "-1.0794,-146.667"
 "-1.0793,-126.667"
 "-1.0792,-96.6667"

Как видно, мы получили вектор из строк. А теперь добавим к каждой строке квадратные скобки для того, чтобы получить набор команд на добавления элементов в вектор, и выполним эти операции.

In [ ]:
DataVec = eval.(Meta.parse.("[".*Data.*"]"))
Out[0]:
9-element Vector{Vector{Float64}}:
 [-1.08, -96.6667]
 [-1.0799, -126.667]
 [-1.0798, -143.333]
 [-1.0797, -166.667]
 [-1.0796, -176.667]
 [-1.0795, -166.667]
 [-1.0794, -146.667]
 [-1.0793, -126.667]
 [-1.0792, -96.6667]

Как видно, в результате мы получили набор векторов, состоящий из 2 элементов, каждая строка выполнилась отдельно.

Следующий пример, который мы рассмотрим, – это исполнение TXT-файла.

In [ ]:
txt = open(io->read(io, String), "$(@__DIR__)/data.txt") # Читаем TXT
Out[0]:
"1.0\n15.726465174717665\n118.19779715124804\n564.5945977782826\n1922.5628210505968\n4961.486649014338\n10069.27911225147\n16457.781390988064\n22003.338138220104\n24301.413576125095\n22293.787678765948\n17018.378514886626\n10791.5437594143\n5653.533013094259\n2423.11034196715\n836.6005210914026\n227.22904721973316\n46.794190857389\n6.873719057490497\n0.6421978090261691\n0.028701721170992484"

Как видно, мы получили строку с числами и переходами на новую строку в текстовом документе после каждого значения. Далее заменяем \n на запятые и оборачиваем строку в [].

In [ ]:
data_str = "[" * replace(txt, "\n" => ",") * "]"
Out[0]:
"[1.0,15.726465174717665,118.19779715124804,564.5945977782826,1922.5628210505968,4961.486649014338,10069.27911225147,16457.781390988064,22003.338138220104,24301.413576125095,22293.787678765948,17018.378514886626,10791.5437594143,5653.533013094259,2423.11034196715,836.6005210914026,227.22904721973316,46.794190857389,6.873719057490497,0.6421978090261691,0.028701721170992484]"

Тепреь строка представляет собой простое задание вектора. Преобразуем строку в выражение и выполним выражение, чтобы получить вектор.

In [ ]:
data_vector = eval(Meta.parse(data_str))
Out[0]:
21-element Vector{Float64}:
     1.0
    15.726465174717665
   118.19779715124804
   564.5945977782826
  1922.5628210505968
  4961.486649014338
 10069.27911225147
 16457.781390988064
 22003.338138220104
 24301.413576125095
 22293.787678765948
 17018.378514886626
 10791.5437594143
  5653.533013094259
  2423.11034196715
   836.6005210914026
   227.22904721973316
    46.794190857389
     6.873719057490497
     0.6421978090261691
     0.028701721170992484

В результате мы получили вектор из 21 значения, с которым можем дальше взаимодействовать как с обычным вектором, например, округлить его элементы до целых чисел.

In [ ]:
plot(eval(Meta.parse("round.(data_vector)")))
Out[0]:

Вывод

В этом примере мы изучили три ключевые функции Julia: parse, Meta.parse, и eval, которые позволяют эффективно работать с данными и кодом, представленными в виде строк.

Эти три функции вместе предоставляют мощный набор инструментов для работы с данными и динамическим кодом, что особенно важно в задачах обработки текстов, автоматизации и создания гибких решений. Проведённые нами эксперименты с разными файлами показали эффективность этих инструментов для решения задач с разными типами входных данных.