Symbole w Julii są takie same jak w Lisp, Scheme czy Ruby. Jednak moim zdaniem odpowiedzi na te pytania nie są do końca satysfakcjonujące . Jeśli przeczytasz te odpowiedzi, wydaje się, że powodem, dla którego symbol różni się od łańcucha, jest to, że łańcuchy są zmienne, podczas gdy symbole są niezmienne, a symbole są również „internowane” - cokolwiek to znaczy. Tak się składa, że ciągi znaków są zmienne w Rubim i Lispie, ale nie ma ich w Julii, a ta różnica to tak naprawdę czerwony śledź. Fakt, że symbole są internowane - tj. Haszowane przez implementację języka w celu szybkiego porównania równości - jest również nieistotnym szczegółem implementacji. Możesz mieć implementację, która nie internuje symboli, a język byłby dokładnie taki sam.
Więc czym tak naprawdę jest symbol? Odpowiedź tkwi w czymś, co łączy Julię i Lisp - umiejętność reprezentowania kodu języka jako struktury danych w samym języku. Niektórzy nazywają to „homoikonicznością” ( Wikipedia ), ale inni nie uważają, że sam język wystarczy, aby język był homoikoniczny. Ale terminologia tak naprawdę nie ma znaczenia. Chodzi o to, że jeśli język może reprezentować swój własny kod, potrzebuje sposobu reprezentowania rzeczy, takich jak przypisania, wywołania funkcji, rzeczy, które można zapisać jako wartości dosłowne itp. Potrzebny jest również sposób reprezentowania własnych zmiennych. Oznacza to, że potrzebujesz sposobu, aby przedstawić - jako dane - foo
po lewej stronie tego:
foo == "foo"
Teraz dochodzimy do sedna sprawy: różnica między symbolem a łańcuchem to różnica między foo
po lewej stronie tego porównania a "foo"
po prawej stronie. Po lewej stronie foo
znajduje się identyfikator, którego wynikiem jest wartość powiązana ze zmienną foo
w bieżącym zakresie. Po prawej stronie "foo"
znajduje się literał łańcuchowy, którego wynikiem jest wartość ciągu „foo”. Symbol w Lisp i Julia to sposób, w jaki reprezentujesz zmienną jako dane. Ciąg po prostu reprezentuje siebie. Możesz zobaczyć różnicę, stosując się eval
do nich:
julia> eval(:foo)
ERROR: foo not defined
julia> foo = "hello"
"hello"
julia> eval(:foo)
"hello"
julia> eval("foo")
"foo"
To, do czego :foo
szacowany jest symbol, zależy od tego, do czego - jeśli w ogóle - foo
jest przypisana zmienna , podczas gdy "foo"
zawsze wylicza się jako „foo”. Jeśli chcesz konstruować wyrażenia w Julii, które używają zmiennych, to używasz symboli (czy o tym wiesz, czy nie). Na przykład:
julia> ex = :(foo = "bar")
:(foo = "bar")
julia> dump(ex)
Expr
head: Symbol =
args: Array{Any}((2,))
1: Symbol foo
2: String "bar"
typ: Any
To, co wyrzucone rzeczy, pokazuje, między innymi, to, że :foo
wewnątrz obiektu wyrażenia znajduje się obiekt symbolu, który uzyskuje się poprzez cytowanie kodu foo = "bar"
. Oto kolejny przykład konstruowania wyrażenia z symbolem :foo
przechowywanym w zmiennej 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"
Jeśli spróbujesz to zrobić, gdy sym
jest powiązany z łańcuchem "foo"
, to nie zadziała:
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""
Jest całkiem jasne, dlaczego to nie zadziała - jeśli spróbujesz przypisać "foo" = "bar"
ręcznie, również nie zadziała.
To jest istota symbolu: symbol jest używany do reprezentowania zmiennej w metaprogramowaniu. Kiedy już masz symbole jako typ danych, kuszące staje się użycie ich do innych celów, takich jak klucze mieszające. Ale jest to przypadkowe, oportunistyczne użycie typu danych, które ma inny główny cel.
Zwróć uwagę, że jakiś czas temu przestałem mówić o Rubim. Dzieje się tak, ponieważ Ruby nie jest homoiconic: Ruby nie reprezentuje swoich wyrażeń jako obiektów Rubiego. Tak więc typ symbolu Ruby jest rodzajem szczątkowego organu - pozostałej adaptacji, odziedziczonej po Lispie, ale nie jest już używana do swojego pierwotnego celu. Symbole Rubiego zostały dokooptowane do innych celów - jako klucze haszujące, aby wyciągnąć metody z tabel metod - ale symbole w Rubim nie są używane do reprezentowania zmiennych.
Jeśli chodzi o to, dlaczego symbole są używane w DataFrames, a nie w łańcuchach, to dlatego, że jest to powszechny wzorzec w DataFrames, który wiąże wartości kolumn ze zmiennymi wewnątrz wyrażeń dostarczonych przez użytkownika. Dlatego naturalne jest, że nazwy kolumn są symbolami, ponieważ symbole są dokładnie tym, czego używasz do przedstawiania zmiennych jako danych. Obecnie musisz pisać, df[:foo]
aby uzyskać dostęp do foo
kolumny, ale w przyszłości możesz mieć do niej dostęp jako df.foo
zamiast tego. Kiedy stanie się to możliwe, tylko kolumny, których nazwy są prawidłowymi identyfikatorami, będą dostępne przy użyciu tej wygodnej składni.
Zobacz też: