ทำไมสำหรับแต่ละคนมีเครื่องหมายทวิภาคแทนที่จะเป็น "ใน"


9

จากคู่มือภาษา Java 5 :

เมื่อคุณเห็นเครื่องหมายจุดคู่ (:) อ่านเป็น "ใน"

ทำไมไม่ใช้inตั้งแต่แรกล่ะ?

สิ่งนี้บักฉันมาหลายปีแล้ว เพราะมันไม่สอดคล้องกับภาษาที่เหลือ ยกตัวอย่างเช่นใน Java มีimplements, extends, superสำหรับความสัมพันธ์ระหว่างชนิดแทนสัญลักษณ์เช่นใน C ++, Scala หรือทับทิม

ใน Java ลำไส้ใหญ่ใช้ใน 5 บริบท ทั้งสามได้รับมรดกจากซีและอีกสองคนได้รับการรับรองโดย Joshua Bloch อย่างน้อยนั่นก็คือเขาพูดในระหว่างการพูดคุยเรื่อง "การปิดการทะเลาะวิวาท" สิ่งนี้เกิดขึ้นเมื่อเขาวิพากษ์วิจารณ์การใช้งานโคลอนสำหรับการทำแผนที่ไม่สอดคล้องกับความหมายในแต่ละครั้ง ซึ่งสำหรับฉันดูเหมือนว่าแปลกเพราะมันเป็นรูปแบบที่คาดหวังสำหรับแต่ละทารุณกรรม กดไลค์list_name/category: elementsหรือlaberl/term: meaning

ฉันสอดแนมรอบ jcp และ jsr แต่ไม่พบว่าไม่มีเครื่องหมายของรายการส่งเมล ไม่พบการสนทนาในเรื่องนี้โดย google forเพียงมือใหม่สับสนโดยความหมายของลำไส้ใหญ่


ข้อโต้แย้งหลักต่อที่inมีให้:

  • ต้องการคำหลักใหม่ และ
  • lexing ซับซ้อน

ลองดูคำจำกัดความของไวยากรณ์ที่เกี่ยวข้อง:

คำให้การ
    : คำสั่ง 'สำหรับ' '(' forControl ')'
    | ...
    ;

forControl
    : EnhancedForControl
    | forInit? ';' การแสดงออก? ';' forUpdate?
    ;

enhancedForControl
    : variableModifier * type variableDeclaratorId ':' นิพจน์
    ;

เปลี่ยนจาก:เป็นinไม่เพิ่มความซับซ้อนหรือต้องการคำหลักใหม่


1
แหล่งที่ดีที่สุดสำหรับการค้นหาแรงจูงใจของนักออกแบบภาษามักจะเป็นตัวออกแบบเอง ที่กล่าวว่านี้เห็นได้ชัดว่าเป็นเพียงน้ำตาล syntactic มากกว่า iterable; ดูstackoverflow.com/questions/11216994/…
Robert Harvey

คำตอบ:


8

ตัวแยกวิเคราะห์ปกติตามที่สอนโดยทั่วไปแล้วจะมีขั้นตอนเล็ก ๆ lexer (เช่น "สแกนเนอร์" หรือ "tokenizer") สับอินพุตเป็นโทเค็นขนาดเล็กที่ใส่คำอธิบายประกอบด้วยชนิด สิ่งนี้อนุญาตให้ parser หลักใช้โทเค็นเป็นองค์ประกอบเทอร์มินัลแทนที่จะต้องปฏิบัติต่อตัวละครแต่ละตัวเป็นเทอร์มินัลซึ่งนำไปสู่การเพิ่มประสิทธิภาพที่เห็นได้ชัดเจน โดยเฉพาะอย่างยิ่ง lexer ยังสามารถลบความคิดเห็นและพื้นที่สีขาวทั้งหมด อย่างไรก็ตามขั้นตอน tokenizer ที่แยกต่างหากหมายความว่าไม่สามารถใช้คำหลักเป็นตัวระบุได้ (ยกเว้นภาษาที่รองรับการลูบซึ่งค่อนข้างไม่เป็นที่นิยมหรือนำหน้าตัวระบุทั้งหมดที่มีเครื่องหมายคล้าย$foo)

ทำไม? สมมติว่าเรามีโทเค็นง่าย ๆ ที่เข้าใจโทเค็นต่อไปนี้:

FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'

โทเค็นไนเซอร์จะตรงกับโทเค็นที่ยาวที่สุดเสมอและต้องการคำหลักมากกว่าตัวระบุ ดังนั้นinterestingจะเป็น lexed IDENT:interestingแต่inจะเป็น lexed ไม่เคยเป็นIN IDENT:interestingข้อมูลโค้ดชอบ

for(var in expression)

จะถูกแปลเป็นโทเค็นสตรีม

FOR LPAREN IDENT:var IN IDENT:expression RPAREN

จนถึงตอนนี้ก็ใช้งานได้แล้ว แต่ตัวแปรใด ๆ ที่inจะถูกเรียกเป็นคำหลักINแทนที่จะเป็นตัวแปรซึ่งจะทำลายรหัส lexer ไม่รักษาสถานะใด ๆ ระหว่างโทเค็นและไม่สามารถรู้ได้ว่าinควรเป็นตัวแปรยกเว้นเมื่อเราอยู่ในลูป นอกจากนี้รหัสต่อไปนี้ควรถูกกฎหมาย:

for(in in expression)

ครั้งแรกinจะเป็นตัวระบุที่สองจะเป็นคำหลัก

มีสองปฏิกิริยาต่อปัญหานี้:

คำหลักตามบริบททำให้เกิดความสับสนลองนำคำหลักมาใช้แทน

Java มีคำที่สงวนไว้มากมายซึ่งบางคำไม่มีประโยชน์ยกเว้นการให้ข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์กับโปรแกรมเมอร์ที่เปลี่ยนมาใช้ Java จาก C ++ การเพิ่มรหัสแบ่งคำค้นหาใหม่ การเพิ่มคำหลักตามบริบททำให้ผู้อ่านเข้าใจรหัสเว้นแต่จะมีการเน้นไวยากรณ์ที่ดีและทำให้การใช้เครื่องมือทำได้ยากเพราะจะต้องใช้เทคนิคการแยกวิเคราะห์ขั้นสูง (ดูด้านล่าง)

เมื่อเราต้องการขยายภาษาวิธีการที่มีเหตุผลเดียวคือใช้สัญลักษณ์ที่ก่อนหน้านี้ไม่ถูกกฎหมายในภาษา โดยเฉพาะอย่างยิ่งสิ่งเหล่านี้ไม่สามารถระบุได้ ด้วยไวยากรณ์วนลูป foreach, Java นำ:คำสำคัญที่มีอยู่กลับมาใช้ใหม่ด้วยความหมายใหม่ ด้วย lambdas, Java ได้เพิ่ม->คำหลักที่ไม่สามารถเกิดขึ้นก่อนหน้านี้ในโปรแกรมทางกฎหมายใด ๆ ( -->จะยังคงเป็น lexed ตาม'--' '>'ที่ถูกกฎหมายและ->อาจถูก lexed ก่อนหน้านี้เป็น'-', '>'แต่ลำดับนั้นจะถูกปฏิเสธโดย parser)

คำหลักตามบริบทลดความซับซ้อนของภาษามาใช้พวกเขา

Lexers มีประโยชน์อย่างแน่นอน แต่แทนที่จะเรียกใช้ lexer ก่อน parser เราสามารถเรียกใช้พวกมันควบคู่กับ parser ตัวแยกวิเคราะห์จากด้านล่างจะทราบชุดของประเภทโทเค็นที่ยอมรับได้ในตำแหน่งที่กำหนดเสมอ parser จากนั้นสามารถร้องขอ lexer ให้ตรงกับประเภทใด ๆ เหล่านี้ที่ตำแหน่งปัจจุบัน ในแต่ละลูป parser จะอยู่ในตำแหน่งที่แสดงโดย·ในไวยากรณ์ (ง่าย) หลังจากที่ตัวแปรพบ:

for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'

ที่ตำแหน่งนั้นโทเค็นที่ถูกกฎหมายจะมีSEMICOLONหรือINไม่มีIDENTก็ได้ คำหลักinจะไม่คลุมเครือทั้งหมด

ในตัวอย่างนี้ตัวแยกวิเคราะห์จากบนลงล่างจะไม่มีปัญหาเช่นกันเนื่องจากเราสามารถเขียนไวยากรณ์ข้างต้นเป็น

for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest =  · ';' expression ';' expression
for_loop_rest = · 'in' expression

และโทเค็นทั้งหมดที่จำเป็นสำหรับการตัดสินใจสามารถมองเห็นได้โดยไม่ต้องย้อนรอย

พิจารณาการใช้งาน

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

for (in in in in())
for (in in : in())

(หมายเหตุ: Java มีเนมสเปซแยกต่างหากสำหรับชื่อประเภทตัวแปรและวิธีการฉันคิดว่านี่เป็นข้อผิดพลาดส่วนใหญ่ไม่ได้หมายความว่าการออกแบบภาษาในภายหลังจะต้องเพิ่มความผิดพลาดมากขึ้น )

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


17

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


2
ดูเหมือนว่า ... ไม่สะดวก มันอนุมานว่านักออกแบบภาษานั้นดีพอที่จะคาดการณ์คำค้นหาส่วนใหญ่ที่ต้องการตั้งแต่เริ่มต้น ฉันไม่แน่ใจว่ามันจำเป็น คอมไพเลอร์ที่ดีสามารถระบุได้ว่าคำหลักเป็นตัวแปรตามบริบทหรือไม่
Robert Harvey

2
ฉันไม่คิดว่า Java มีคำหลักตามบริบทอย่าง C # ดังนั้นการใช้inจะมีความหมายที่จะแนะนำคำหลักใหม่ซึ่งจะทำลายความเข้ากันได้ย้อนหลัง ( System.inใคร?) หรือแนะนำแนวคิดใหม่เอี่ยมที่ไม่รู้จักก่อนหน้านี้ (คำหลักเชิงบริบท) ทั้งหมดเพื่อสิ่งที่ได้รับ?
Jörg W Mittag

2
คำหลักเชิงบริบทมีอันตรายอะไรบ้าง
user2418306

5
@ user2418306 การเพิ่มคำหลักไม่จำเป็นต้องทำลายรหัสที่มีอยู่โดยที่ภาษานั้นไม่ได้แยกวิเคราะห์ด้วยขั้นตอน lexer แยกต่างหาก โดยเฉพาะอย่างยิ่ง "ใน" ในfor(variable in expression)ไม่สามารถคลุมเครือกับรหัสทางกฎหมายใด ๆ แม้ว่า "ใน" สามารถใช้สำหรับตัวแปร อย่างไรก็ตาม lexer phase แยกเป็นเรื่องธรรมดาในคอมไพเลอร์ toolchains สิ่งนี้จะทำให้เป็นไปไม่ได้หรืออย่างน้อยก็ยากที่จะแยกวิเคราะห์ Java ด้วยตัวแยกวิเคราะห์ทั่วไป การทำให้ไวยากรณ์ของภาษาเรียบง่ายมักจะดีสำหรับทุกคนที่เกี่ยวข้อง ไม่ใช่ทุกคนที่ต้องการความประหลาดใจทางไวยากรณ์เช่น C ++ หรือ Perl
amon

1
@RobertHarvey: อย่าลืมสิ่งนั้นconstและgotoเป็นทั้งคำที่สงวนไว้ใน Java แต่ยังไม่ได้ใช้ (ยัง)
TMN
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.