Что такое "символ" у Юлии?
В частности: я пытаюсь использовать пакет DataFrames Джулии, в частности функцию readtable() с опцией names, но для этого требуется вектор символов.
- что такое символ?
- почему они выбирают это по вектору строк?
До сих пор я нашел лишь несколько ссылок на символ слова в языке Юлия. Кажется, что символы представлены как ":var", но мне далеко не ясно, что они собой представляют.
В сторону: я могу бежать
df = readtable( "table.txt", names = [symbol("var1"), symbol("var2")] )
Мои два маркированных вопроса все еще стоят.
1 ответ
Символы в Julia такие же, как в Lisp, Scheme или Ruby. Однако, на мой взгляд, ответы на эти смежные вопросы не совсем удовлетворительные. Если вы читаете эти ответы, кажется, что причина, по которой символ отличается от строки, состоит в том, что строки являются изменяемыми, в то время как символы неизменяемы, а символы также "интернированы" - что бы это ни значило. Строки действительно изменчивы в Ruby и Lisp, но их нет в Julia, и эта разница на самом деле является красной сельдью. Тот факт, что символы интернированы - т.е. хешируется языковой реализацией для быстрых сравнений на равенство - также не имеет значения для реализации. Вы можете иметь реализацию, которая не интернирует символы, и язык будет точно таким же.
Так что же такое символ на самом деле? Ответ заключается в том, что объединяет Джулию и Лисп - способность представлять код языка как структуру данных в самом языке. Некоторые люди называют это "гомойконичностью" ( Википедия), но другие, кажется, не думают, что одного языка достаточно для того, чтобы язык был гомойконичным. Но терминология на самом деле не имеет значения. Дело в том, что когда язык может представлять свой собственный код, ему нужен способ представления таких вещей, как присваивания, вызовы функций, вещи, которые можно записать в виде буквальных значений, и т. Д. Ему также нужен способ представления своих собственных переменных. То есть вам нужен способ представления - в качестве данных - foo
на левой стороне этого:
foo == "foo"
Теперь мы подошли к сути вопроса: разница между символом и строкой - это разница между foo
на левой стороне этого сравнения и "foo"
на правой стороне. Налево, foo
является идентификатором, и он оценивается в значение, связанное с переменной foo
в текущем объеме. Справа, "foo"
является строковым литералом, и он оценивается как строковое значение "foo". Символ как в Лиспе, так и в Юлии - это то, как вы представляете переменную как данные. Строка просто представляет себя. Вы можете увидеть разницу, применив eval
им:
julia> eval(:foo)
ERROR: foo not defined
julia> foo = "hello"
"hello"
julia> eval(:foo)
"hello"
julia> eval("foo")
"foo"
Какой символ :foo
оценивает, зависит от того, что - если что-нибудь - переменная foo
связан, тогда как "foo"
всегда просто оценивает "foo". Если вы хотите построить выражения в Julia, которые используют переменные, то вы используете символы (знаете ли вы это или нет). Например:
julia> ex = :(foo = "bar")
:(foo = "bar")
julia> dump(ex)
Expr
head: Symbol =
args: Array{Any}((2,))
1: Symbol foo
2: String "bar"
typ: Any
То, что вывалили из всего этого, показывает, среди прочего, что есть :foo
объект символа внутри объекта выражения, который вы получаете, цитируя код foo = "bar"
, Вот еще один пример, построение выражения с символом :foo
хранится в переменной sym
:
julia> sym = :foo
:foo
julia> eval(sym)
"hello"
julia> ex = :($sym = "bar"; 1 + 2)
:(begin
foo = "bar"
1 + 2
end)
julia> eval(ex)
3
julia> foo
"bar"
Если вы попытаетесь сделать это, когда sym
привязан к строке "foo"
не сработает
julia> sym = "foo"
"foo"
julia> ex = :($sym = "bar"; 1 + 2)
:(begin
"foo" = "bar"
1 + 2
end)
julia> eval(ex)
ERROR: syntax: invalid assignment location ""foo""
Довольно понятно, почему это не сработает - если вы попытались назначить "foo" = "bar"
вручную это тоже не сработает.
В этом суть символа: символ используется для представления переменной в метапрограммировании. Разумеется, когда у вас есть символы в качестве типа данных, становится заманчивым использовать их для других целей, например, в качестве хеш-ключей. Но это случайное, оппортунистическое использование типа данных, имеющего другую основную цель.
Обратите внимание, что я перестал говорить о Руби некоторое время назад. Это потому, что Ruby не гомоичен: Ruby не представляет свои выражения как объекты Ruby. Таким образом, символьный тип Руби является своего рода рудиментарным органом - оставшейся адаптацией, унаследованной от Лиспа, но более не используемой для его первоначальной цели. Символы Ruby использовались для других целей - в качестве ключей хеширования, для извлечения методов из таблиц методов - но символы в Ruby не используются для представления переменных.
Что касается того, почему символы используются в DataFrames, а не в строках, то это потому, что в DataFrames распространена схема связывания значений столбцов с переменными внутри пользовательских выражений. Поэтому имена столбцов являются символами, поскольку символы представляют собой именно то, что вы используете для представления переменных в виде данных. В настоящее время вы должны написать df[:foo]
чтобы получить доступ к foo
столбец, но в будущем вы сможете получить к нему доступ как df.foo
вместо. Когда это станет возможным, с этим удобным синтаксисом будут доступны только столбцы, имена которых являются действительными идентификаторами.
Смотрите также:
Что касается исходного вопроса на данный момент, то есть версии 0.21 (и в будущем) DataFrames.jl позволяет как
Symbol
s и строки, которые будут использоваться в качестве имен столбцов, так как это не проблема для поддержки обоих и в разных ситуациях либо
Symbol
или строка может быть предпочтительнее для пользователя.
Вот пример:
julia> using DataFrames
julia> df = DataFrame(:a => 1:2, :b => 3:4)
2×2 DataFrame
│ Row │ a │ b │
│ │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1 │ 1 │ 3 │
│ 2 │ 2 │ 4 │
julia> DataFrame("a" => 1:2, "b" => 3:4) # this is the same
2×2 DataFrame
│ Row │ a │ b │
│ │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1 │ 1 │ 3 │
│ 2 │ 2 │ 4 │
julia> df[:, :a]
2-element Array{Int64,1}:
1
2
julia> df[:, "a"] # this is the same
2-element Array{Int64,1}:
1
2