ทำไมผู้สร้าง Ruby จึงเลือกใช้แนวคิดของ Symbols


15

tl; dr: จะมีคำจำกัดความเกี่ยวกับสัญลักษณ์ของผู้ไม่เชื่อเรื่องภาษาและมีเหตุผลที่จะใช้มันในภาษาอื่น ๆ หรือไม่?

ดังนั้นทำไมผู้สร้าง Ruby จึงใช้แนวคิดsymbolsในภาษา

ฉันถามสิ่งนี้จากมุมมองของโปรแกรมเมอร์ที่ไม่ใช่ทับทิม ฉันได้เรียนรู้จำนวนมากภาษาอื่น ๆ symbolsและพบว่าในไม่มีของพวกเขาจำเป็นที่จะต้องระบุถ้าผมจัดการหรือไม่กับสิ่งที่โทรทับทิม

คำถามหลักคือแนวคิดของsymbolsRuby มีอยู่ในการแสดงหรือไม่หรือเป็นเพียงบางสิ่งที่จำเป็นเนื่องจากวิธีการเขียนภาษา?

โปรแกรมใน Ruby จะเบาลงและ / หรือเร็วกว่าสมมติว่า Python หรือ Javascript เป็นคู่กันหรือไม่ ถ้าเป็นเช่นนั้นมันจะเป็นเพราะsymbolsอะไร?

เนื่องจากหนึ่งในความตั้งใจของรูบี้คือการอ่านและเขียนสำหรับมนุษย์ได้ง่ายผู้สร้างมันไม่สามารถปลดเปลื้องกระบวนการเข้ารหัสโดยใช้การปรับปรุงเหล่านั้นในล่ามเอง (เพราะอาจเป็นภาษาอื่น)

ดูเหมือนว่าทุกคนต้องการที่จะรู้ว่าอะไรsymbolsคืออะไรและใช้งานอย่างไรและทำไมพวกเขาถึงอยู่ที่นั่นในตอนแรก


Scala มีสัญลักษณ์อยู่ด้านบนของหัวของฉัน ฉันคิดว่า Lisps หลายคนทำ
D. Ben Knoble

คำตอบ:


17

ผู้สร้าง Ruby, Yukihiro "Matz" Matsumoto โพสต์คำอธิบายเกี่ยวกับวิธีที่ Ruby ได้รับอิทธิพลจาก Lisp, Smalltalk, Perl (และ Wikipedia บอกว่า Ada and Eiffel ด้วย):

Ruby เป็นภาษาที่ออกแบบในขั้นตอนต่อไปนี้:

  • ใช้ภาษาเสียงกระเพื่อมอย่างง่าย (เช่นภาษาก่อน CL)
  • ลบแมโคร s-expression
  • เพิ่มระบบวัตถุอย่างง่าย (ง่ายกว่า CLOS)
  • เพิ่มบล็อกแรงบันดาลใจจากฟังก์ชั่นการสั่งซื้อที่สูงขึ้น
  • เพิ่มวิธีการที่พบใน Smalltalk
  • เพิ่มฟังก์ชันการทำงานที่พบใน Perl (ในวิธี OO)

ในทางทฤษฎีแล้วทับทิมเป็นเสียงกระเพื่อม

เรียกมันว่า MatzLisp ต่อจากนี้เป็นต้นไป ;-)

ในคอมไพเลอร์ใด ๆ คุณจะจัดการตัวระบุสำหรับฟังก์ชั่นตัวแปรชื่อบล็อกประเภทและอื่น ๆ โดยทั่วไปแล้วคุณเก็บไว้ในคอมไพเลอร์และลืมเกี่ยวกับพวกเขาในการปฏิบัติการผลิตยกเว้นเมื่อคุณเพิ่มข้อมูลการแก้จุดบกพร่อง

ใน Lisp สัญลักษณ์ดังกล่าวเป็นทรัพยากรชั้นหนึ่งซึ่งโฮสต์ในแพ็คเกจที่แตกต่างกันซึ่งหมายความว่าคุณสามารถเพิ่มสัญลักษณ์ใหม่ได้ที่รันไทม์ผูกกับวัตถุประเภทอื่น สิ่งนี้มีประโยชน์เมื่อการเขียนโปรแกรมเมตาเพราะคุณมั่นใจได้ว่าคุณจะไม่ได้ตั้งชื่อการชนกับส่วนอื่น ๆ ของรหัส

นอกจากนี้สัญลักษณ์ยังถูกใช้ในการอ่านและสามารถเปรียบเทียบได้ด้วยตัวตนซึ่งเป็นวิธีที่มีประสิทธิภาพในการมีค่าชนิดใหม่(เช่นตัวเลข แต่เป็นนามธรรม) วิธีนี้จะช่วยเขียนโค้ดที่คุณใช้ค่าสัญลักษณ์โดยตรงแทนที่จะกำหนดประเภท enum ของคุณซึ่งสำรองไว้ด้วยจำนวนเต็ม นอกจากนี้แต่ละสัญลักษณ์สามารถเก็บข้อมูลเพิ่มเติมได้ นั่นเป็นวิธีที่ตัวอย่างเช่น Emacs / Slime สามารถแนบข้อมูลเมตาจาก Emacs ลงในรายการคุณสมบัติของสัญลักษณ์

ความคิดของสัญลักษณ์เป็นศูนย์กลางในเสียงกระเพื่อม ดูตัวอย่างที่PAIP (กระบวนทัศน์ของการเขียนโปรแกรมปัญญาประดิษฐ์: กรณีศึกษาใน Common LISP, Norvig) สำหรับตัวอย่างโดยละเอียด


5
คำตอบที่ดี. อย่างไรก็ตามฉันไม่เห็นด้วยกับ Matz: ฉันจะไม่คิดถึงการโทรหาภาษาโดยไม่ต้องใช้ภาษามาโครเสียงกระเพื่อม สิ่งอำนวยความสะดวกของ metaprogramming ของ lisp นั้นเป็นสิ่งที่ทำให้ภาษานี้มีพลังที่ยอดเยี่ยมโดยสร้างขึ้นจากไวยากรณ์ที่เรียบง่ายและไร้ประสิทธิภาพ
cmaster - คืนสถานะโมนิกา

11

ดังนั้นทำไมผู้สร้าง Ruby จึงต้องใช้แนวคิดsymbolsในภาษา?

พวกเขาไม่ได้ "ต้อง" อย่างเคร่งครัดพวกเขาเลือกที่จะ นอกจากนี้โปรดทราบว่าการพูดอย่างเคร่งครัดSymbolไม่ได้เป็นส่วนหนึ่งของภาษา แต่เป็นส่วนหนึ่งของห้องสมุดหลัก พวกเขาจะมีภาษาระดับไวยากรณ์ที่แท้จริง Symbol::newแต่พวกเขาจะทำงานก็เช่นกันถ้าคุณต้องสร้างให้พวกเขาโดยการเรียก

ฉันถามจากมุมมองของโปรแกรมเมอร์ที่ไม่ใช่ทับทิมที่พยายามเข้าใจ ฉันได้เรียนรู้จำนวนมากและภาษาอื่น ๆ symbolsที่พบในไม่มีของพวกเขาจำเป็นที่จะต้องระบุถ้าผมจัดการหรือไม่กับสิ่งที่โทรทับทิม

คุณไม่ได้พูดว่า "ภาษาอื่น ๆ อีกมากมาย" คืออะไร แต่นี่เป็นเพียงส่วนย่อของภาษาที่มีSymbolประเภทข้อมูลเช่น Ruby:

  • ECMAScriptเช่นเดียวกับJavaScript
  • สกาล่า
  • Scheme , Common Lisp , Clojure (เช่นกัน, Clojure มีคำหลัก ) โดยทั่วไปทุกภาษาในตระกูล Lisp, ผู้สืบทอดและญาติของมันมี
  • Smalltalk , Newspeakและภาษาอื่น ๆ อีกมากมายในตระกูล Smalltalk (ซึ่งน่าจะเป็นที่ที่ Ruby ได้มาจากพวกเขา) รวมถึงObjective-C (แม้ว่าจะอยู่ในรูปแบบที่ จำกัด )
  • Erlang (เรียกว่าอะตอม )เช่นElixirและLFE
  • จูเลีย
  • อารัมภบท (เรียกว่าอะตอม ) ซึ่งน่าจะเป็นที่ที่ Erlang ได้มาจากพวกมัน

นอกจากนี้ยังมีภาษาอื่น ๆ ที่ให้คุณสมบัติของSymbols ในรูปแบบที่แตกต่างกัน ใน Java เช่นคุณสมบัติของทับทิมของStrings จะถูกแบ่งออกเป็นสอง (ที่จริงสาม) ประเภทStringและ/StringBuilder StringBufferบนมืออื่น ๆ , คุณสมบัติของทับทิมของSymbolชนิดพับลงใน Java Stringประเภท: Java Strings สามารถฝึกงาน , สตริงตัวอักษรและStrings ซึ่งเป็นผลมาจากการรวบรวมเวลาการประเมินการแสดงออกคงมีการฝึกงานโดยอัตโนมัติสร้างแบบไดนามิกStrings สามารถฝึกงานโดยการเรียกString.internวิธี การฝึกงานStringใน Java นั้นเหมือนกับSymbolใน Ruby แต่ไม่ได้นำมาใช้เป็นประเภทแยกต่างหากเป็นเพียงสถานะที่แตกต่างที่ JavaStringสามารถอยู่ได้ (หมายเหตุ: ในทับทิมรุ่นก่อนหน้านี้String#to_symเคยถูกเรียกใช้String#internและวิธีการดังกล่าวยังคงมีอยู่ในปัจจุบันในฐานะนามแฝงดั้งเดิม)

คำถามหลักอาจเป็น: แนวคิดของsymbolsRuby มีอยู่ในฐานะเจตนาการปฏิบัติงานเหนือตนเองและภาษาอื่น ๆ หรือไม่

Symbols เป็นครั้งแรกและสำคัญที่สุดประเภทข้อมูลที่เฉพาะเจาะจงกับความหมาย ความหมายเหล่านี้ยังทำให้สามารถดำเนินการการปฏิบัติงานของนักแสดงบางคนได้ (เช่นการทดสอบความเท่าเทียมกันอย่างรวดเร็ว O (1)) แต่นั่นไม่ใช่จุดประสงค์หลัก

หรือสิ่งที่จำเป็นต้องมีอยู่เพราะวิธีการเขียนภาษา?

Symbolไม่จำเป็นต้องใช้ภาษา Ruby เลย Ruby จะทำงานได้ดีหากไม่มีพวกเขา พวกเขาล้วนมีคุณสมบัติห้องสมุด มีที่เดียวในภาษาที่เชื่อมโยงกับSymbols: defนิพจน์นิยามเมธอดจะประเมินค่าเพื่อSymbolแสดงชื่อของวิธีการที่ถูกกำหนด อย่างไรก็ตามนั่นเป็นการเปลี่ยนแปลงที่ค่อนข้างเร็ว ๆ นี้ก่อนหน้านั้นค่าส่งคืนนั้นไม่ได้ระบุเพียง MRI ประเมินเพียงแค่nilRubinius ประเมินว่าเป็นRubinius::CompiledMethodวัตถุและอื่น ๆ นอกจากนี้ยังจะเป็นไปได้ในการประเมินไปยังUnboundMethod... Stringหรือเพียงแค่

โปรแกรมใน Ruby จะเบาลงและ / หรือเร็วกว่านั้นหรือไม่สมมติว่า Python หรือ Node ถ้าเป็นเช่นนั้นมันจะเป็นเพราะsymbolsอะไร?

ฉันไม่แน่ใจว่าคุณถามอะไรที่นี่ ประสิทธิภาพส่วนใหญ่เป็นเรื่องของคุณภาพการใช้งานไม่ใช่ภาษา นอกจากนี้โหนดยังไม่ได้เป็นภาษา แต่เป็นเฟรมเวิร์ก I / O ที่มีเหตุการณ์สำหรับ ECMAScript การเรียกใช้สคริปต์ที่เทียบเท่าบน IronPython และ MRI นั้น IronPython นั้นน่าจะเร็วกว่า การรันสคริปต์ที่เทียบเท่าบน CPython และ JRuby + Truffle, JRuby + Truffle น่าจะเร็วกว่า สิ่งนี้ไม่เกี่ยวกับSymbols แต่ด้วยคุณภาพของการนำไปใช้งาน: JRuby + Truffle มีคอมไพเลอร์ที่ปรับให้เหมาะสมอย่างจริงจังรวมถึงเครื่องมือเพิ่มประสิทธิภาพทั้งหมดของ JVM ประสิทธิภาพสูง CPython เป็นล่ามง่าย

เนื่องจากหนึ่งในความตั้งใจของรูบี้คือการอ่านและเขียนง่ายสำหรับมนุษย์ผู้สร้างของมันไม่สามารถลดขั้นตอนการเขียนโค้ดโดยใช้การปรับปรุงเหล่านั้นในล่ามเอง (เพราะอาจเป็นภาษาอื่น)

ไม่Symbols ไม่ได้เป็นการเพิ่มประสิทธิภาพของคอมไพเลอร์ พวกเขาเป็นประเภทข้อมูลแยกต่างหากที่มีความหมายเฉพาะ พวกเขาไม่เหมือนฟลานุมของ YARV ซึ่งเป็นการเพิ่มประสิทธิภาพภายในส่วนตัวสำหรับFloats สถานการณ์ไม่ได้เช่นเดียวกับInteger, BignumและFixnumซึ่งควรจะมีรายละเอียดการเพิ่มประสิทธิภาพการมองไม่เห็นส่วนตัวภายใน แต่น่าเสียดายที่ไม่ได้เป็น (ซึ่งในที่สุดก็จะได้รับการแก้ไขในทับทิม 2.4 ซึ่งจะลบFixnumและBignumและใบเพียงInteger.)

การทำในแบบที่ Java ทำนั้นเป็นสถานะพิเศษของStrings ปกติหมายความว่าคุณจำเป็นต้องระวังเสมอว่าStrings ของคุณอยู่ในสถานะพิเศษนั้นหรือไม่และภายใต้สถานการณ์ใดที่พวกเขาอยู่ในสถานะพิเศษนั้นโดยอัตโนมัติและเมื่อใด นั่นเป็นภาระที่สูงกว่าการมีประเภทข้อมูลแยกต่างหาก

จะมีคำจำกัดความเกี่ยวกับสัญลักษณ์ของผู้ไม่เชื่อเรื่องภาษาและมีเหตุผลในการใช้ภาษาอื่นหรือไม่?

Symbolเป็นประเภทข้อมูลที่หมายถึงแนวคิดของการที่ชื่อหรือฉลาก Symbols คือวัตถุที่มีค่าไม่เปลี่ยนรูปมักจะทันที สองSymbols ชมีค่าเท่ากันนอกจากนี้ยังมีการรับประกันที่จะเหมือนกันในคำอื่น ๆ สองSymbols Symbolซึ่งเท่ากับเป็นจริงหนึ่งเดียวกัน ซึ่งหมายความว่าความเท่าเทียมกันของคุณค่าและความเท่าเทียมกันของการอ้างอิงเป็นสิ่งเดียวกันดังนั้นความเสมอภาคนั้นมีประสิทธิภาพและ O (1)

เหตุผลที่ทำให้พวกเขามีภาษาเหมือนกันจริงๆเป็นอิสระจากภาษา บางภาษาพึ่งพาพวกเขามากกว่าคนอื่น

ตัวอย่างเช่นในตระกูล Lisp ไม่มีแนวคิดของ "ตัวแปร" คุณมีSymbolความเกี่ยวข้องกับค่าต่างๆแทน

ในภาษาที่มีความสามารถในการสะท้อนแสงหรือครุ่นคิดSymbolมักจะใช้เพื่อแสดงถึงชื่อของหน่วยงานที่สะท้อนให้เห็นใน APIs สะท้อนเช่นในทับทิมObject#methods, Object#singleton_methods, Object#public_methods, Object#protected_methodsและObject#public_methodsกลับArrayของSymbols (แม้ว่าพวกเขาจะได้ก็เช่นกันกลับArrayของMethods) Object#public_sendใช้Symboldenoting ชื่อของข้อความเพื่อส่งเป็นอาร์กิวเมนต์ (แม้ว่าจะยอมรับStringเช่นกันSymbolก็มีความหมายถูกต้องมากกว่า)

ใน ECMAScript Symbols เป็นหน่วยการสร้างพื้นฐานของการทำให้ ECMAScript มีความปลอดภัยในอนาคต พวกเขายังมีบทบาทสำคัญในการสะท้อน


อะตอมของ Erlang ถูกนำมาโดยตรงจาก Prolog (Robert Virding บอกฉันว่าในบางจุด)
Zachary K

2

สัญลักษณ์มีประโยชน์ใน Ruby และคุณจะเห็นพวกมันทั่วรหัส Ruby เพราะสัญลักษณ์แต่ละอันจะถูกใช้ซ้ำทุกครั้งที่ถูกอ้างอิง นี่คือการปรับปรุงประสิทธิภาพเหนือสตริงเนื่องจากการใช้สตริงที่ไม่ได้บันทึกในตัวแปรแต่ละครั้งจะสร้างวัตถุใหม่ในหน่วยความจำ ตัวอย่างเช่นถ้าฉันใช้สตริงเดียวกันหลายครั้งเป็นคีย์แฮช:

my_hash = {"a" => 1, "b" => 2, "c" => 3}
100_000.times { |i| puts my_hash["a"] }

สตริง "a" ถูกสร้างขึ้นในหน่วยความจำ 101,000 ครั้ง ถ้าฉันใช้สัญลักษณ์แทน:

my_hash = {a: 1, b: 2, c: 3}
100_000.times { |i| puts my_hash[:a] }

สัญลักษณ์:aยังคงเป็นวัตถุหนึ่งในหน่วยความจำ สิ่งนี้ทำให้สัญลักษณ์มีประสิทธิภาพมากกว่าสตริงอย่างมากมาย

UPDATE นี่คือมาตรฐาน (นำมาจากCodecademy ) ที่แสดงให้เห็นถึงประสิทธิภาพที่แตกต่าง:

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]

string_time = Benchmark.realtime do
  100_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  100_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

นี่คือผลลัพธ์ของฉันสำหรับ MBP ของฉัน:

String time: 0.1254125550040044 seconds.
Symbol time: 0.07360960397636518 seconds.

มีความแตกต่างที่ชัดเจนในการใช้สตริงกับสัญลักษณ์เพื่อระบุคีย์ในแฮช


ฉันไม่แน่ใจว่าเป็นกรณีนี้หรือไม่ ฉันคาดหวังว่าการติดตั้ง Ruby เพื่อรันรหัสเดียวกันหลาย ๆ ครั้งไม่ใช่การแยกวิเคราะห์รหัสซ้ำแล้วซ้ำอีกสำหรับการทำซ้ำแต่ละครั้ง แม้ว่าการเกิดคำศัพท์แต่ละครั้ง"a"จะเป็นสายอักขระใหม่ฉันคิดว่าในตัวอย่างของคุณจะมีสองสิ่ง"a"(และการนำไปปฏิบัติอาจแบ่งปันความจำได้จนกว่าจะมีการกลายพันธุ์) ในการสร้างสตริงเป็นล้านคุณอาจต้องใช้ String.new ("a") แต่ฉันไม่เชี่ยวชาญใน Ruby ดังนั้นบางทีฉันผิด
coredump

1
ในหนึ่งในบทเรียนของ Codecademy พวกเขาสร้างมาตรฐานสำหรับสตริงเทียบกับสัญลักษณ์เหมือนตัวอย่างของฉัน ฉันจะเพิ่มลงในคำตอบ
Keith Mattix

1
ขอขอบคุณที่เพิ่มมาตรฐาน การทดสอบของคุณแสดงกำไรที่คาดว่าจะได้รับจากการใช้สัญลักษณ์แทนสตริงเนื่องจากการทดสอบที่เร็วขึ้นใน hashtable (การเปรียบเทียบตัวตนกับการเปรียบเทียบสตริง) แต่ไม่มีวิธีที่เราสามารถอนุมานว่าสตริงนั้นได้รับการจัดสรรในแต่ละการทำซ้ำ ฉันเพิ่มเวอร์ชันด้วย string_AZ[String.new("r")]เพื่อดูว่ามันสร้างความแตกต่างหรือไม่ ฉันได้รับ 21ms สำหรับสตริง (เวอร์ชั่นดั้งเดิม), 7ms พร้อมสัญลักษณ์และ 50ms พร้อมสตริงใหม่ในแต่ละครั้ง ดังนั้นฉันจะบอกว่าสตริงไม่ได้รับการจัดสรรมากกับ"r"เวอร์ชั่นตามตัวอักษร
coredump

1
อาดังนั้นฉันจึงขุดขึ้นมาบ้างและใน Ruby 2.1 มีการแบ่งปันสตริงในความเป็นจริง เห็นได้ชัดว่าฉันพลาดการอัปเดตนั้น ขอบคุณสำหรับการชี้ให้เห็นว่า กลับไปที่คำถามเดิมฉันคิดว่ามาตรฐานทั้งสองแสดงประโยชน์ของสัญลักษณ์เทียบกับสตริง
Keith Mattix
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.