web-dev-qa-db-fra.com

Qu'est-ce qu'un "symbole" chez Julia?

Plus précisément: j'essaie d'utiliser le package DataFrames de Julia, en particulier la fonction readtable () avec l'option names, mais cela nécessite un vecteur de symboles.

  • qu'est-ce qu'un symbole?
  • pourquoi choisiraient-ils cela plutôt qu'un vecteur de chaînes?

Jusqu'à présent, je n'ai trouvé qu'une poignée de références au symbole Word dans la langue Julia. Il semble que les symboles soient représentés par ": var", mais il est loin d'être clair pour moi ce qu'ils sont.

À part: je peux courir

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

Mes deux questions à puces sont toujours d'actualité.

103
Mageek

Les symboles dans Julia sont les mêmes que dans LISP, Scheme ou Ruby. Cependant, les réponses à ces questions connexes ne sont pas vraiment satisfaisantes , à mon avis. Si vous lisez ces réponses, il semble que la raison pour laquelle un symbole est différent d'une chaîne est que les chaînes sont mutables tandis que les symboles sont immuables, et les symboles sont également "internés" - quoi que cela signifie. Les chaînes peuvent être mutables dans Ruby et LISP, mais elles ne le sont pas dans Julia, et cette différence est en fait un hareng rouge. Le fait que les symboles soient internés - c'est-à-dire hachés par l'implémentation du langage) pour des comparaisons rapides d'égalité - est également un détail d'implémentation non pertinent. Vous pouvez avoir une implémentation qui n'intègre pas de symboles et le langage serait exactement le même.

Alors qu'est-ce qu'un symbole, vraiment? La réponse réside dans quelque chose que Julia et LISP ont en commun - la capacité de représenter le code de la langue comme une structure de données dans la langue elle-même. Certaines personnes appellent cela "homoiconicity" ( Wikipedia ), mais d'autres ne semblent pas penser que seul est suffisant pour qu'une langue soit homoiconique. Mais la terminologie n'a pas vraiment d'importance. Le fait est que lorsqu'un langage peut représenter son propre code, il a besoin d'un moyen de représenter des choses comme des affectations, des appels de fonction, des choses qui peuvent être écrites sous forme de valeurs littérales, etc. Il a également besoin d'un moyen de représenter ses propres variables. C'est-à-dire, vous avez besoin d'un moyen de représenter - en tant que données - le foo sur le côté gauche de ceci:

foo == "foo"

Maintenant, nous allons au cœur du problème: la différence entre un symbole et une chaîne est la différence entre foo sur le côté gauche de cette comparaison et "foo" sur le côté droit. À gauche, foo est un identifiant et il évalue la valeur liée à la variable foo dans la portée actuelle. Sur la droite, "foo" est un littéral de chaîne et il prend la valeur de chaîne "foo". Un symbole dans LISP et Julia est la façon dont vous représentez une variable en tant que données. Une chaîne se représente simplement. Vous pouvez voir la différence en leur appliquant eval:

Julia> eval(:foo)
ERROR: foo not defined

Julia> foo = "hello"
"hello"

Julia> eval(:foo)
"hello"

Julia> eval("foo")
"foo"

Qu'est-ce que le symbole :foo évalue à dépend de quoi - le cas échéant - la variable foo est liée, tandis que "foo" évalue toujours juste "foo". Si vous voulez construire des expressions dans Julia qui utilisent des variables, alors vous utilisez des symboles (que vous le sachiez ou non). Par exemple:

Julia> ex = :(foo = "bar")
:(foo = "bar")

Julia> dump(ex)
Expr
  head: Symbol =
  args: Array{Any}((2,))
    1: Symbol foo
    2: String "bar"
  typ: Any

Ce que cela montre, entre autres, c'est qu'il y a un :foo objet symbole à l'intérieur de l'objet d'expression que vous obtenez en citant le code foo = "bar". Voici un autre exemple, la construction d'une expression avec le symbole :foo stocké dans la variable 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"

Si vous essayez de le faire lorsque sym est lié à la chaîne "foo", ça ne marchera pas:

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""

Il est assez clair de voir pourquoi cela ne fonctionnera pas - si vous avez essayé d'assigner "foo" = "bar" à la main, cela ne fonctionnera pas non plus.

C'est l'essence d'un symbole: un symbole est utilisé pour représenter une variable dans la métaprogrammation. Une fois que vous avez des symboles comme type de données, bien sûr, il devient tentant de les utiliser pour d'autres choses, comme des clés de hachage. Mais c'est une utilisation fortuite et opportuniste d'un type de données qui a un autre objectif principal.

Notez que j'ai cessé de parler de Ruby il y a quelque temps. C'est parce que Ruby n'est pas homoiconique: Ruby ne représente pas ses expressions comme Ruby. Le type de symbole Ruby est donc une sorte d'organe résiduel - une adaptation restante, héritée de LISP, mais qui n'est plus utilisée à sa destination d'origine. Ruby ont été co- opté à d'autres fins - comme clés de hachage, pour extraire les méthodes des tables de méthodes - mais les symboles dans Ruby ne sont pas utilisés pour représenter les variables.

Quant à savoir pourquoi les symboles sont utilisés dans les DataFrames plutôt que les chaînes, c'est parce que c'est un modèle courant dans DataFrames pour lier les valeurs des colonnes aux variables à l'intérieur des expressions fournies par l'utilisateur. Il est donc naturel que les noms de colonnes soient des symboles, car les symboles sont exactement ce que vous utilisez pour représenter des variables sous forme de données. Actuellement, vous devez écrire df[:foo] pour accéder à la colonne foo, mais à l'avenir, vous pourrez peut-être y accéder en tant que df.foo au lieu. Lorsque cela devient possible, seules les colonnes dont les noms sont des identificateurs valides seront accessibles avec cette syntaxe pratique.

Voir aussi:

187
StefanKarpinski