สัญลักษณ์ใน Julia จะเหมือนกับใน Lisp, Scheme หรือ Ruby อย่างไรก็ตามในความคิดของฉันคำตอบสำหรับคำถามที่เกี่ยวข้องนั้นไม่เป็นที่น่าพอใจนัก หากคุณอ่านคำตอบเหล่านั้นดูเหมือนว่าสาเหตุที่สัญลักษณ์แตกต่างจากสตริงก็คือสตริงนั้นไม่แน่นอนในขณะที่สัญลักษณ์ไม่เปลี่ยนรูปและสัญลักษณ์ยังเป็น "ภายใน" ไม่ว่าจะหมายถึงอะไรก็ตาม สตริงเกิดขึ้นได้ใน Ruby และ Lisp แต่ไม่ได้อยู่ใน Julia และความแตกต่างนั้นเป็นปลาเฮอริ่งสีแดง ความจริงที่ว่าสัญลักษณ์ถูก จำกัด ไว้ - นั่นคือการแฮชโดยการใช้ภาษาเพื่อการเปรียบเทียบความเท่าเทียมกันอย่างรวดเร็ว - ยังเป็นรายละเอียดการใช้งานที่ไม่เกี่ยวข้อง คุณสามารถนำไปใช้งานที่ไม่มีสัญลักษณ์ฝึกงานและภาษาจะเหมือนกันทุกประการ
แล้วสัญลักษณ์คืออะไร? คำตอบอยู่ที่สิ่งที่ Julia และ Lisp มีเหมือนกันนั่นคือความสามารถในการแสดงรหัสของภาษาเป็นโครงสร้างข้อมูลในภาษานั้นเอง บางคนเรียกสิ่งนี้ว่า "homoiconicity" ( Wikipedia ) แต่บางคนดูเหมือนจะไม่คิดว่าภาษาเดียวก็เพียงพอแล้วสำหรับภาษาที่จะเป็น homoiconic แต่ศัพท์มันไม่สำคัญหรอก ประเด็นก็คือเมื่อภาษาสามารถแทนรหัสของตัวเองได้จำเป็นต้องมีวิธีในการแสดงสิ่งต่างๆเช่นการกำหนดการเรียกใช้ฟังก์ชันสิ่งที่สามารถเขียนเป็นค่าตามตัวอักษร ฯลฯ นอกจากนี้ยังต้องการวิธีในการแทนตัวแปรของตัวเอง กล่าวคือคุณต้องมีวิธีแสดง - เป็นข้อมูล - 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
foo = "bar"
นี่คืออีกตัวอย่างหนึ่งการสร้างนิพจน์โดยใช้สัญลักษณ์ที่:foo
เก็บไว้ในตัวแปร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"
หากคุณพยายามทำสิ่งนี้เมื่อ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"
ด้วยมือก็จะไม่ได้ผลเช่นกัน
นี่คือสาระสำคัญของสัญลักษณ์: สัญลักษณ์ใช้แทนตัวแปรใน metaprogramming เมื่อคุณมีสัญลักษณ์เป็นประเภทข้อมูลแน่นอนว่ามันจะดึงดูดให้ใช้กับสิ่งอื่น ๆ เช่นเป็นแฮชคีย์ แต่นั่นเป็นการใช้ประเภทข้อมูลโดยบังเอิญและฉวยโอกาสซึ่งมีวัตถุประสงค์หลักอื่น
สังเกตว่าฉันเลิกพูดถึง Ruby ไปสักพักแล้ว นั่นเป็นเพราะ Ruby ไม่ใช่ homoiconic: Ruby ไม่ได้แสดงนิพจน์เป็นวัตถุ Ruby ประเภทสัญลักษณ์ของรูบี้จึงเป็นอวัยวะร่องรอยซึ่งเป็นสิ่งดัดแปลงที่เหลือสืบทอดมาจากลิสป์ แต่ไม่ได้ใช้เพื่อจุดประสงค์เดิมอีกต่อไป สัญลักษณ์ Ruby ได้รับการเลือกร่วมกันเพื่อวัตถุประสงค์อื่นเช่นแฮชคีย์เพื่อดึงเมธอดออกจากตารางวิธีการ - แต่สัญลักษณ์ใน Ruby ไม่ได้ใช้เพื่อแสดงตัวแปร
สาเหตุที่ใช้สัญลักษณ์ใน DataFrames แทนที่จะเป็นสตริงนั้นเป็นเพราะรูปแบบทั่วไปใน DataFrames เพื่อผูกค่าคอลัมน์กับตัวแปรภายในนิพจน์ที่ผู้ใช้กำหนด ดังนั้นจึงเป็นเรื่องธรรมดาที่ชื่อคอลัมน์จะเป็นสัญลักษณ์เนื่องจากสัญลักษณ์เป็นสิ่งที่คุณใช้แทนตัวแปรเป็นข้อมูล ขณะนี้คุณต้องเขียนdf[:foo]
เพื่อเข้าถึงfoo
คอลัมน์ แต่ในอนาคตคุณอาจสามารถเข้าถึงได้df.foo
แทน เมื่อเป็นไปได้เฉพาะคอลัมน์ที่มีชื่อเป็นตัวระบุที่ถูกต้องเท่านั้นที่สามารถเข้าถึงได้ด้วยไวยากรณ์ที่สะดวกนี้
ดูสิ่งนี้ด้วย: