преобразовать выражение Julia в массив?

#julia

Вопрос:

я получаю список, который был сохранен в виде строки, и пытаюсь разобрать его для использования. Я могу использовать Meta.parse, чтобы превратить строку в выражение :

 li = db.list
print(typeof(d[1]),d[1])
>> String
   ["apple", "banana", "coconut"]

parsed_li = Meta.parse(d[1])
print(typeof(parsed_li), parsed_li)
>> Expr
   ["apple", "banana", "coconut"]
 

но не похоже, что я могу использовать его как обычный список; например, с помощью обычного списка я мог бы получить доступ к элементу по его индексу, но я не могу получить доступ к индексу объекта Expr :

 normal_li = ["apple", "banana", "coconut"]
print(normal_li[1])
>> apple

print(parsed_li[1])
>>
MethodError: no method matching getindex(::Expr, ::Int64)

Stacktrace:
 [1] top-level scope at In[214]:8

 

я возился, не уверен, чего мне здесь не хватает!

Ответ №1:

Полагаться на Meta.parse() синтаксический анализ ваших входных данных неэффективно и небезопасно (вредоносный код всегда может быть вставлен во входные данные, и вы можете что-то делать eval с ним и т. Д.). Вместо этого вы предпочитаете использовать инструменты, предназначенные именно для этого. В вашем случае просто обратите внимание, что эти данные можно рассматривать как допустимый формат JSON и их можно прочитать следующим образом:

 
julia> str = """["apple", "banana", "coconut"]"""
"["apple", "banana", "coconut"]"

julia> using JSON3

julia> JSON3.read(str)
3-element JSON3.Array{String, Base.CodeUnits{UInt8, String}, Vector{UInt64}}:
 "apple"
 "banana"
 "coconut"

julia> collect(JSON3.read(str))
3-element Vector{String}:
 "apple"
 "banana"
 "coconut"
 

Если вы доверяете входным данным (обычно вы никогда не должны), вы можете сделать это небезопасным способом:

 julia> eval(Meta.parse(str))
3-element Vector{String}:
 "apple"
 "banana"
 "coconut"
 

Если вы хотите извлечь информацию без eval , это можно сделать с помощью args поля Expr объекта — однако способ его обработки будет каждый раз сильно зависеть от структуры данных:

 julia> Meta.parse(str).args
3-element Vector{Any}:
 "apple"
 "banana"
 "coconut"
 

Комментарии:

1. Хороший момент о вредоносном вводе. Поверхностной проверки Expr заголовка недостаточно даже для гарантии eval безопасности. Например, parsed_li.head есть vect , и можно подумать: «ну, это не гигантский блок кода или метод, это просто массив». Однако это тоже массив : ["apple", delete_all_your_files_and_steal_your_cat(), "coconut"] .

Ответ №2:

Это всегда внизу на странице документов: evaled_li = eval(parsed_li) будет вектором строки.

Ответ №3:

Expr это не структура данных, подобная списку.

Цитируется по документу

Expr

Тип, представляющий составные выражения в проанализированном коде Julia (ASTs). Каждое выражение состоит из head Symbol указания, к какому виду выражения оно относится (например, вызов, цикл for, условный оператор и т.д.), И подвыражений (например, аргументы вызова). Подвыражения хранятся в вызываемом Vector{Any} поле args .

Вы все равно можете получить доступ к этим элементам через args поле, хотя, если хотите.

Комментарии:

1. да, мой вопрос был в том, можно ли его преобразовать в массив

2. Хм, я думал, было ясно, что args поле — это то, что вы хотите в данном конкретном случае? В противном случае я не видел ничего осмысленного в преобразовании двоичной древовидной структуры в список здесь.

3. parsed_li.args на самом деле является вектором Any. Выражение еще не было вычислено, поэтому оно args будет содержать векторы литералов (String, Int, Float64), Symbol или другого выражения. Поскольку ваш массив изначально содержал только строки, этого было бы достаточно.