在Julia中,“symbol”是什么?

203

具体来说: 我正在尝试使用Julia的DataFrames包,具体而言是使用readtable()函数和names选项,但这需要一个符号向量。

  • 什么是符号?
  • 为什么他们选择它而不是字符串向量?

到目前为止,我只发现了几个关于Julia语言中符号的参考资料。它似乎是由“:var”表示的,但我并不清楚它们是什么。

附注: 我可以运

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

我的两个带项目符号的问题仍然存在。


3
以下是这个主题的一些讨论内容:https://groups.google.com/d/msg/julia-users/MS7KW8IU-0o/cQ-yDOs_CQEJ - jverzani
2个回答

342
在Julia中,符号与Lisp、Scheme或Ruby中的符号相同。然而,对于那些相关问题的答案,我认为并不是真正令人满意。如果你阅读那些答案,似乎符号与字符串不同的原因是字符串是可变的,而符号是不可变的,并且符号也是“内部化”的——不管那意味着什么。在Ruby和Lisp中,字符串确实是可变的,但在Julia中却不是这样,而这种差异实际上是一个误导。符号被内部化——即由语言实现进行哈希处理以进行快速的相等比较——也是一个无关紧要的实现细节。你可以有一个不内部化符号的实现,而语言本身不会有任何变化。
所以,什么是符号呢?答案在于Julia和Lisp有一个共同点 - 就是能够将语言的代码表示为语言本身的数据结构。有些人称之为"同像性"维基百科),但其他人似乎认为这一点还不足以使一种语言成为同像性的。但术语并不重要。关键是,当一种语言能够表示自己的代码时,它需要一种方式来表示赋值、函数调用、可以写成字面值的东西等等。它还需要一种方式来表示自己的变量。也就是说,你需要一种方式来将这个左边的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 = "bar"后,表达式对象中有一个:foo符号对象。这里还有另一个例子,使用存储在变量sym中的符号:foo构建一个表达式。
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的符号类型有点像是一种退化的器官——一种从Lisp继承而来的遗传适应,但不再用于其原始目的。Ruby的符号已经被用于其他目的——作为哈希键,从方法表中提取方法——但在Ruby中,符号不用于表示变量。
关于为什么在DataFrames中使用符号而不是字符串,这是因为通常将列值绑定到用户提供的表达式中的变量内部。因此,列名自然应该是符号,因为符号正是您用来表示变量作为数据的方式。目前,您必须编写df[:foo]来访问foo列,但在将来,您可能可以使用df.foo来访问它。当这成为可能时,只有那些名称是有效标识符的列才能使用这种便捷的语法进行访问。
另请参阅:
- [Julia官方文档:元编程](https://docs.julialang.org/en/v1/manual/metaprogramming/) - [类似Elixir和Julia的语言在什么意义上是同像的?](https://link2)

9
在计算机科学中,“字符串驻留”是一种存储每个不同的不可改变字符串值的唯一副本的方法。字符串驻留使得某些字符串处理任务在时间或空间效率上更高效,但需要在创建或驻留字符串时付出更多时间的代价。 - xiaodai
2
你在某个地方写了 eval(:foo),而在另一个地方写了 eval(sym)eval(:foo)eval(foo) 之间有什么实质性的区别吗? - Grayscale
10
非常正确:eval(:foo) 返回变量 foo 所绑定的值,而 eval(foo) 对该值调用 eval。在全局作用域中,写成 eval(:foo) 相当于只写 foo,因此 eval(foo) 就像是 eval(eval(:foo)) - StefanKarpinski
3
现在可能的是,未来你可以通过 df.foo 访问它。 - jling
2
未来就在眼前。 - StefanKarpinski
显示剩余2条评论

15

在参考原问题(即0.21版本发布时以及未来),DataFrames.jl允许使用Symbol和字符串作为列名,因为同时支持这两种类型并且在不同情况下用户可能更喜欢使用Symbol或字符串,并没有问题。

以下是一个示例:

julia> using DataFrames

julia> df = DataFrame(:a => 1:2, :b => 3:4)
2×2 DataFrame
│ Row │ a     │ b     │
│     │ Int64Int64 │
├─────┼───────┼───────┤
│ 113     │
│ 224     │

julia> DataFrame("a" => 1:2, "b" => 3:4) # this is the same
2×2 DataFrame
│ Row │ a     │ b     │
│     │ Int64Int64 │
├─────┼───────┼───────┤
│ 113     │
│ 224     │

julia> df[:, :a]
2-element Array{Int64,1}:
 1
 2

julia> df[:, "a"] # this is the same
2-element Array{Int64,1}:
 1
 2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接