Che cos'è un "simbolo" in Julia?


131

In particolare: sto cercando di utilizzare il pacchetto DataFrames di Julia, in particolare la funzione readtable () con l'opzione names, ma che richiede un vettore di simboli.

  • che cos'è un simbolo?
  • perché lo sceglierebbero su un vettore di stringhe?

Finora ho trovato solo una manciata di riferimenti alla parola simbolo in lingua Julia. Sembra che i simboli siano rappresentati da ": var", ma per me è tutt'altro che chiaro cosa siano.

A parte: posso correre

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

Le mie due domande puntate sono ancora valide.


3
Alcune conversazioni su questo argomento sono disponibili qui: groups.google.com/d/msg/julia-users/MS7KW8IU-0o/cQ-yDOs_CQEJ
jverzani,

Risposte:


231

I simboli in Julia sono gli stessi di Lisp, Scheme o Ruby. Tuttavia, a mio avviso , le risposte a tali domande non sono davvero soddisfacenti . Se leggi quelle risposte, sembra che la ragione per cui un simbolo è diverso da una stringa è che le stringhe sono mutabili mentre i simboli sono immutabili, e anche i simboli vengono "internati", qualunque cosa significhi. Le stringhe sono mutabili in Ruby e Lisp, ma non sono in Julia, e quella differenza è in realtà un'aringa rossa. Anche il fatto che i simboli siano internati, ovvero l'hash implementato dal linguaggio per confronti rapidi di uguaglianza, è anche un dettaglio di implementazione irrilevante. Potresti avere un'implementazione che non contenga simboli e la lingua sarebbe esattamente la stessa.

Quindi cos'è un simbolo, davvero? La risposta sta in qualcosa che Julia e Lisp hanno in comune: la capacità di rappresentare il codice della lingua come una struttura di dati nella lingua stessa. Alcune persone chiamano questa "omoiconicità" ( Wikipedia ), ma altri non sembrano pensare che da soli sia sufficiente che una lingua sia omoiconica. Ma la terminologia non ha molta importanza. Il punto è che quando una lingua può rappresentare il proprio codice, ha bisogno di un modo per rappresentare cose come assegnazioni, chiamate di funzioni, cose che possono essere scritte come valori letterali, ecc. Ha anche bisogno di un modo per rappresentare le proprie variabili. Cioè, hai bisogno di un modo per rappresentare - come dati - il foolato sinistro di questo:

foo == "foo"

Ora stiamo arrivando al nocciolo della questione: la differenza tra un simbolo e una stringa è la differenza tra foosul lato sinistro di quel confronto e "foo"sul lato destro. A sinistra, fooè un identificatore e valuta il valore associato alla variabile foonell'ambito corrente. A destra, "foo"è una stringa letterale e restituisce il valore di stringa "pippo". Un simbolo in Lisp e in Julia è il modo in cui rappresenti una variabile come dati. Una stringa rappresenta solo se stessa. Puoi vedere la differenza applicandoti evala loro:

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

julia> foo = "hello"
"hello"

julia> eval(:foo)
"hello"

julia> eval("foo")
"foo"

La :foovalutazione del simbolo dipende da quale - se non altro - la variabile fooè vincolata, mentre "foo"valuta sempre "pippo". Se vuoi costruire espressioni in Julia che usano variabili, allora stai usando dei simboli (che tu lo sappia o no). Per esempio:

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

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

Ciò che quella roba scaricata mostra, tra le altre cose, è che c'è un :foooggetto simbolo all'interno dell'oggetto espressione che ottieni citando il codice foo = "bar". Ecco un altro esempio, la costruzione di un'espressione con il simbolo :foomemorizzato nella variabile 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"

Se provi a farlo quando symè associato alla stringa "foo", non funzionerà:

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

È abbastanza chiaro capire perché questo non funzionerà - se hai provato ad assegnare "foo" = "bar"a mano, anche non funzionerebbe.

Questa è l'essenza di un simbolo: un simbolo viene utilizzato per rappresentare una variabile nella metaprogrammazione. Una volta che hai i simboli come tipo di dati, ovviamente, diventa tentato di usarli per altre cose, come le chiavi hash. Ma questo è un uso accidentale e opportunistico di un tipo di dati che ha un altro scopo primario.

Nota che ho smesso di parlare di Ruby qualche tempo fa. Questo perché Ruby non è omoiconico: Ruby non rappresenta le sue espressioni come oggetti Ruby. Quindi il tipo di simbolo di Ruby è una specie di organo vestigiale - un adattamento residuo, ereditato da Lisp, ma non più utilizzato per il suo scopo originale. I simboli Ruby sono stati cooptati per altri scopi - come chiavi hash, per estrarre i metodi dalle tabelle dei metodi - ma i simboli in Ruby non sono usati per rappresentare le variabili.

Il motivo per cui i simboli vengono utilizzati in DataFrames piuttosto che nelle stringhe, è perché è un modello comune in DataFrames per associare i valori di colonna alle variabili all'interno delle espressioni fornite dall'utente. Quindi è naturale che i nomi delle colonne siano simboli, poiché i simboli sono esattamente ciò che usi per rappresentare le variabili come dati. Al momento, devi scrivere df[:foo]per accedere alla foocolonna, ma in futuro potresti essere in grado di accedervi come df.fooinvece. Quando ciò diventa possibile, solo le colonne i cui nomi sono identificatori validi saranno accessibili con questa comoda sintassi.

Guarda anche:


6
Interning: in informatica, l'interning delle stringhe è un metodo per memorizzare solo una copia di ogni valore di stringa distinto, che deve essere immutabile. Il interning delle stringhe rende alcune attività di elaborazione delle stringhe più efficienti in termini di tempo o spazio a costo di richiedere più tempo quando la stringa viene creata o internata. en.wikipedia.org/wiki/String_interning
xiaodai,

Ad un certo punto scrivi eval(:foo)e ad un altro eval(sym). C'è una differenza significativa tra eval(:foo)e eval(foo)?
Scala di grigi

Assolutamente: eval(:foo)dà valore a cui fooè legata la variabile mentre eval(foo)chiama eval su quel valore. Scrivere eval(:foo)equivale a solo foo(nell'ambito globale), quindi eval(foo)è come eval(eval(:foo)).
StefanKarpinski,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.