Что такое "символ" в Джулии?

В частности: Я пытаюсь использовать пакет Julia DataFrames, в частности функцию readtable() с параметром names, но для этого требуется вектор символов.

  • что такое символ?
  • почему они выбирают это над вектором строк?

До сих пор я нашел лишь несколько ссылок на символ слова на языке Джулии. Кажется, что символы представлены ": var", но мне далеко не ясно, что они собой представляют.

Кроме: Я могу запустить

df = readtable( "table.txt", names = [symbol("var1"), symbol("var2")] )

Мои две маркированные вопросы все еще стоят.

Ответ 1

Символы в Julia такие же, как в Lisp, Scheme или Ruby. Тем не менее, ответы на эти связанные вопросы на самом деле не удовлетворительные, на мой взгляд. Если вы прочтете эти ответы, кажется, что причина, по которой символ отличается от строки, состоит в том, что строки изменяемы, а символы неизменяемы, а символы также "интернированы" - что бы это ни значило. Строки действительно изменяются в Ruby и Lisp, но они не в Джулии, и эта разница на самом деле является красной селедкой. Тот факт, что символы интернированы, т.е. Хешируется языковой реализацией для быстрого сравнения сравнений, также является нерелевантной деталью реализации. У вас может быть реализация, которая не является символами-стажерами, и язык будет точно таким же.

Итак, что же такое символ? Ответ заключается в том, что у Джулии и Lisp есть общая способность - представлять языковой код как структуру данных в самом языке. Некоторые люди называют это "homoiconicity" (Wikipedia), но другие надеются, Кажется, кажется, что одного достаточно, чтобы язык был гомоциконным. Но терминология не имеет большого значения. Дело в том, что, когда язык может представлять свой собственный код, ему нужен способ представления таких вещей, как присвоения, вызовы функций, вещи, которые могут быть записаны как буквальные значения и т.д. Ему также нужен способ представления своих собственных переменных. Вам нужен способ представления - как данные - foo в левой части этого:

foo == "foo"

Теперь мы добираемся до сути вопроса: разница между символом и строкой - это разница между foo в левой части этого сравнения и "foo" с правой стороны. Слева, foo является идентификатором, и он оценивает значение, связанное с переменной foo в текущей области. Справа "foo" - строковый литерал, и он оценивает строковое значение "foo". Символ как Lisp, так и Julia - это то, как вы представляете переменную как данные. Строка просто представляет себя. Вы можете увидеть разницу, применив к ним 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 не является homoiconic: Ruby не представляет свои выражения как объекты Ruby. Таким образом, тип символа Ruby - это своего рода рудиментарный орган - остаточная адаптация, унаследованная от Lisp, но более не используемая для его первоначальной цели. Символы Ruby были кооптированы для других целей - в качестве хеш-ключей, чтобы вытащить методы из таблиц методов, но символы в Ruby не используются для представления переменных.

Что касается символов, которые используются в DataFrames, а не в строках, это потому, что общий шаблон в DataFrames связывает значения столбца с переменными внутри выражений, предоставленных пользователем. Поэтому естественно, что имена столбцов являются символами, так как символы - это именно то, что вы используете для представления переменных в качестве данных. В настоящее время вам нужно написать df[:foo] для доступа к столбцу foo, но в будущем вы можете получить к нему доступ как df.foo. Когда это станет возможным, с этим удобным синтаксисом будет доступен только столбцы, имена которых являются действительными идентификаторами.

См. также: