Các biểu tượng trong Julia giống như trong Lisp, Scheme hoặc Ruby. Tuy nhiên, theo tôi, câu trả lời cho những câu hỏi liên quan là không thực sự thỏa đáng . Nếu bạn đọc những câu trả lời đó, có vẻ như lý do một biểu tượng khác với một chuỗi là các chuỗi có thể thay đổi trong khi các biểu tượng là bất biến, và các biểu tượng cũng được "thực hiện" - bất kể điều đó có nghĩa là gì. Các chuỗi tình cờ có thể thay đổi trong Ruby và Lisp, nhưng chúng không ở Julia và sự khác biệt đó thực sự là một cá trích đỏ. Thực tế là các biểu tượng được thực hiện - tức là được băm bằng cách triển khai ngôn ngữ để so sánh công bằng nhanh chóng - cũng là một chi tiết triển khai không liên quan. Bạn có thể có một triển khai không biểu tượng thực tập và ngôn ngữ sẽ giống hệt nhau.
Vì vậy, một biểu tượng, thực sự là gì? Câu trả lời nằm ở một điểm chung mà Julia và Lisp có - khả năng biểu diễn mã của ngôn ngữ dưới dạng cấu trúc dữ liệu trong chính ngôn ngữ. Một số người gọi đây là "đồng âm" ( Wikipedia ), nhưng những người khác dường như không nghĩ rằng một mình là đủ để một ngôn ngữ là đồng âm. Nhưng thuật ngữ không thực sự quan trọng. Vấn đề là khi một ngôn ngữ có thể đại diện cho mã của chính nó, nó cần một cách để biểu diễn những thứ như bài tập, lời gọi hàm, những thứ có thể được viết dưới dạng giá trị bằng chữ, v.v. Nó cũng cần một cách để biểu diễn các biến của chính nó. Tức là, bạn cần một cách để thể hiện - dưới dạng dữ liệu - foo
ở phía bên trái của điều này:
foo == "foo"
Bây giờ chúng ta đang đi vào trọng tâm của vấn đề: sự khác biệt giữa biểu tượng và chuỗi là sự khác biệt giữa foo
ở phía bên trái của so sánh đó và "foo"
ở phía bên phải. Ở bên trái, foo
là một định danh và nó ước tính giá trị được liên kết với biến foo
trong phạm vi hiện tại. Ở bên phải, "foo"
là một chuỗi ký tự và nó ước tính giá trị chuỗi "foo". Một biểu tượng trong cả Lisp và Julia là cách bạn biểu diễn một biến dưới dạng dữ liệu. Một chuỗi chỉ đại diện cho chính nó. Bạn có thể thấy sự khác biệt bằng cách áp dụng eval
cho họ:
julia> eval(:foo)
ERROR: foo not defined
julia> foo = "hello"
"hello"
julia> eval(:foo)
"hello"
julia> eval("foo")
"foo"
Những gì biểu tượng :foo
đánh giá phụ thuộc vào cái gì - nếu có gì - biến foo
bị ràng buộc với, trong khi"foo"
luôn chỉ đánh giá là "foo". Nếu bạn muốn xây dựng các biểu thức trong Julia sử dụng các biến, thì bạn đang sử dụng các ký hiệu (cho dù bạn có biết hay không). Ví dụ:
julia> ex = :(foo = "bar")
:(foo = "bar")
julia> dump(ex)
Expr
head: Symbol =
args: Array{Any}((2,))
1: Symbol foo
2: String "bar"
typ: Any
Những gì đã bỏ đi cho thấy, trong số những thứ khác, là có một :foo
đối tượng biểu tượng bên trong đối tượng biểu thức bạn nhận được bằng cách trích dẫn mã foo = "bar"
. Đây là một ví dụ khác, xây dựng một biểu thức với ký hiệu :foo
được lưu trong biến 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"
Nếu bạn cố gắng làm điều này khi sym
bị ràng buộc với chuỗi"foo"
, nó sẽ không hoạt động:
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""
Thật rõ ràng để biết lý do tại sao điều này sẽ không hoạt động - nếu bạn cố gắng gán "foo" = "bar"
bằng tay, nó cũng sẽ không hoạt động.
Đây là bản chất của một biểu tượng: một biểu tượng được sử dụng để thể hiện một biến trong siêu lập trình. Tất nhiên, khi bạn có các biểu tượng như một kiểu dữ liệu, việc sử dụng chúng cho các mục khác sẽ trở nên hấp dẫn hơn, như là các khóa băm. Nhưng đó là cách sử dụng ngẫu nhiên, cơ hội của một loại dữ liệu có mục đích chính khác.
Lưu ý rằng tôi đã ngừng nói về Ruby một thời gian trước. Đó là bởi vì Ruby không phải là đồng âm: Ruby không biểu thị các biểu thức của nó dưới dạng các đối tượng Ruby. Vì vậy, loại biểu tượng của Ruby là một loại cơ quan tiền đình - một bản chuyển thể còn sót lại, được thừa hưởng từ Lisp, nhưng không còn được sử dụng cho mục đích ban đầu của nó. Các biểu tượng Ruby đã được chọn đồng thời cho các mục đích khác - làm khóa băm, để kéo các phương thức ra khỏi bảng phương thức - nhưng các biểu tượng trong Ruby không được sử dụng để biểu diễn các biến.
Về lý do tại sao các biểu tượng được sử dụng trong DataFrames chứ không phải là chuỗi, bởi vì đó là một mẫu phổ biến trong DataFrames để liên kết các giá trị cột với các biến bên trong các biểu thức do người dùng cung cấp. Vì vậy, việc các tên cột là biểu tượng là điều tự nhiên, vì các biểu tượng chính xác là những gì bạn sử dụng để biểu diễn các biến dưới dạng dữ liệu. Hiện tại, bạn phải viết df[:foo]
để truy cập vào foo
cột, nhưng trong tương lai, bạn có thể truy cập nó df.foo
thay thế. Khi điều đó trở nên có thể, chỉ các cột có tên là định danh hợp lệ mới có thể truy cập được bằng cú pháp thuận tiện này.
Xem thêm: