ข้อดีของไวยากรณ์ภาษาจากซ้ายไปขวา


18

ฉันได้ดูบทสัมภาษณ์ของ Herb Sutter ที่ช่อง 9 และเขาพูดถึงตอนท้ายของวิดีโอว่าไวยากรณ์ของภาษาซ้าย - ขวาจะอยู่ในอันดับต้น ๆ ของนักแปลเพื่อมาตรฐาน C ++ ในอนาคต (แม้ว่าเขาจะยอมรับว่าการดัดแปลง C ++ ด้วยวิธีนั้น จะทำให้สัตว์ร้ายต่างไปจากเดิมอย่างสิ้นเชิง)

นอกเหนือจาก:

  • มนุษย์สามารถเข้าใจได้ชัดเจนขึ้นด้วยตาเปล่าเช่น

    //C syntax
    
    /*pointer to function taking a pointer to function(which takes 2 integers as 
    
    arguments and returns an int), and an int as arguments and returning an int*/
    
    int (*fp)(int (*ff)(int x, int y), int b)
    
    //Go analogous syntax which is left to write
    
    f func(func(int,int) int, int) int
    
  • แยกวิเคราะห์ได้ง่ายขึ้น (นำไปสู่การสนับสนุนเครื่องมือที่ดีกว่าตามที่กล่าวไว้ในวิดีโอ - เช่นการเปลี่ยนรหัสใหม่)

ข้อดีอื่น ๆ ที่มีอยู่กับไวยากรณ์ "จากซ้ายไปขวา" ในภาษาการเขียนโปรแกรม ฉันรู้เพียง Pascal และ Go ใช้ไวยากรณ์ประเภทนั้น (และ Go ไม่ไปตามที่ฉันเข้าใจจากโพสต์บล็อกนี้ซึ่งฉันได้ใช้ตัวอย่าง) ถ้าเป็นไปได้ไหมที่จะมีภาษาโปรแกรมระบบด้วยประเภทนั้น ของไวยากรณ์?


1
Haskell ใช้จากซ้ายไปขวา:f :: (Int -> Int -> Int) -> Int -> Int
Karoly Horvath

1
ActionScript เช่นกัน: function strlen(s:String):int {...}. นอกจากนี้ยังพิมพ์แคลคูลัสแลมบ์ดา (เช่น Haskell)
outis

2
ใครช่วยอธิบายหน่อยได้ไหมว่าปิดการโหวต :) ฉันไม่เห็นเหตุผลในการปิด แต่ฉันอาจถามคำถาม "ผิด"

1
ฉันไม่ได้ลงคะแนนให้ปิด แต่ความคิดเห็นโดย @Devjosh นั้นเหมาะสมกว่าสำหรับโปรแกรมเมอร์หวังว่าจะมีใครบางคนย้ายมันออกไป ....
Nim

3
@ Frank: และอย่าลืมในกรณีที่ตัวชี้ฟังก์ชั่นว่าไวยากรณ์เป็นที่น่าอึดอัดใจเพราะประเภทจริงจะถูกแยกออก ! นั่นเป็นการระเบิดที่ต่ำ ...
Matthieu M.

คำตอบ:


12

ข้อดีพื้นฐานคือการแยกวิเคราะห์นั้นง่ายและไม่เหมือนใคร โปรดทราบว่าหลังจากแยกบรรทัดแล้วคอมไพเลอร์จะรู้ว่าประเภทที่แน่นอนคืออะไรดังนั้นจากตรงนั้นการกำหนดประเภทนั้นไม่เกี่ยวข้อง

ฟังก์ชั่นใด ๆ ที่ส่งคืนอาร์กิวเมนต์ประเภทอาเรย์หรือประเภทของตัวชี้ฟังก์ชั่นนั้นอ่านยาก:

// common way of obtaining the static size in elements of an array:
template <typename T, int N>
char (&array_size_impl( T (&)[N] ))[N];
// alternative parser:
template <typename T, int N>               // this would probably be changed also
array_simple_impl function( T [N] & ) char [N] &;

และจะมีโอกาสน้อยกว่าสำหรับการเข้าใจผิด (เป็นคำที่รบกวนที่สุด ):

// Current C++
type x( another_type() );      // create an instance x of type passing a
                               // default constructed another_type temporary?
                               // or declare a function x that returns type and takes as argument
                               // a function that has no arguments and returns another_type
// How the compiler reads it:
x function( function() another_type ) type;

// What many people mean:
x type { another_type{} };

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


วิธีการเกี่ยวกับการแสดงออก "ซ้ายไปขวาไวยากรณ์" จะส่งผลกระทบต่อลำดับความสำคัญของผู้ปฏิบัติงานและลำดับการประเมินอย่างไร

1
@celavek: ถ้าคุณกลับไปที่การสัมภาษณ์คุณจะทราบว่าเขาไม่ต้องการเปลี่ยนทั้งไวยากรณ์ของภาษาเพียงการประกาศและคำจำกัดความของ s แน่นอนว่าอาจแทรกซึมอยู่ในนิพจน์อื่น ๆ ฉันไม่แน่ใจเกินไปว่าบรรทัดสุดท้ายในตัวอย่างด้านบนมีความเหมาะสมจากซ้ายไปขวา (คิดว่าวิธีการสร้างชั่วคราวนั้นอาจต้องเปลี่ยน ... ใน C # ที่แก้ไขโดยให้สองความหมายแก่newผู้ประกอบการสำหรับstructหรือclassซึ่งไม่สามารถใช้กับ C ++ ใน C ++ ไม่มีความแตกต่างในประเภทค่า / การอ้างอิง
David Rodríguez - dribeas

5

เรามาถึงที่นี่ได้อย่างไร

ไวยากรณ์ C สำหรับการประกาศคะแนนฟังก์ชั่นมีวัตถุประสงค์เพื่อสะท้อนการใช้งาน พิจารณาการประกาศฟังก์ชั่นปกติเช่นนี้จาก<math.h>:

double round(double number);

หากต้องการตัวแปรจุดคุณสามารถกำหนดให้กับประเภทความปลอดภัยโดยใช้

fp = round;

คุณจะต้องประกาศfpตัวแปรนั้นด้วยวิธีนี้:

double (*fp)(double number);

ดังนั้นสิ่งที่คุณต้องทำคือการดูที่วิธีการที่คุณจะใช้ฟังก์ชั่นและเปลี่ยนชื่อของฟังก์ชั่นที่มีการอ้างอิงตัวชี้ทำให้เข้าround *fpอย่างไรก็ตามคุณจำเป็นต้องมีชุดของ parens พิเศษซึ่งบางคนอาจบอกว่าทำให้มันยุ่งยากขึ้น

เนื้อหานี้เคยง่ายกว่าเดิมใน C ดั้งเดิมซึ่งไม่ได้มีลายเซ็นของฟังก์ชั่น แต่ก็อย่ากลับไปที่นั่นใช่ไหม

สถานที่ที่น่ารังเกียจโดยเฉพาะอย่างยิ่งคือการหาวิธีการประกาศฟังก์ชั่นที่ใช้เป็นอาร์กิวเมนต์หรือส่งกลับตัวชี้ไปยังฟังก์ชั่นหรือทั้งสองอย่าง

หากคุณมีฟังก์ชั่น:

void myhandler(int signo);

คุณสามารถส่งผ่านไปยังฟังก์ชั่นสัญญาณ (3) ด้วยวิธีนี้:

signal(SIGHUP, myhandler);

หรือถ้าคุณต้องการเก็บ handler เก่าไว้

old_handler = signal(SIGHUP, new_handler);

ซึ่งค่อนข้างง่าย อะไรคือสิ่งที่เรียบง่าย - ไม่สวยและไม่ง่าย - การประกาศให้ถูกต้อง

signal(int signo, ???)

คุณเพิ่งกลับไปที่การประกาศฟังก์ชั่นและสลับชื่อสำหรับการอ้างอิงจุด:

signal(int sendsig, void (*hisfunc)(int gotsig));

เนื่องจากคุณไม่ได้ประกาศgotsigคุณอาจพบว่าอ่านง่ายกว่าถ้าคุณละเว้น:

signal(int sendsig, void (*hisfunc)(int));

หรืออาจจะไม่ :(

ยกเว้นว่าไม่ดีพอเนื่องจากสัญญาณ (3) จะส่งคืนตัวจัดการเก่าเช่นเดียวกับใน:

old_handler = signal(SIGHUP, new_handler);

ดังนั้นคุณต้องหาวิธีที่จะประกาศสิ่งเหล่านั้นทั้งหมด

void (*old_handler)(int gotsig);

ก็เพียงพอแล้วสำหรับตัวแปรที่คุณจะกำหนดให้ โปรดทราบว่าคุณไม่ได้จริงๆประกาศที่นี่เท่านั้นgotsig old_handlerดังนั้นนี่ก็เพียงพอแล้ว:

void (*old_handler)(int);

นั่นทำให้เรามีคำจำกัดความที่ถูกต้องสำหรับสัญญาณ (3):

void (*signal(int signo, void (*handler)(int)))(int);

พิมพ์เพื่อช่วยเหลือ

มาถึงตอนนี้ฉันคิดว่าทุกคนจะเห็นด้วยว่านั่นเป็นเรื่องยุ่ง บางครั้งการตั้งชื่อนามธรรมของคุณดีกว่า บ่อยครั้งจริงๆ ด้วยสิทธิtypedefสิ่งนี้จะเข้าใจได้ง่ายขึ้นมาก:

typedef void (*sig_t) (int);

ตอนนี้ตัวแปรตัวจัดการของคุณจะกลายเป็น

sig_t old_handler, new_handler;

และการประกาศของคุณสำหรับสัญญาณ (3) กลายเป็นเพียงแค่

sig_t signal(int signo, sig_t handler);

ซึ่งเข้าใจได้โดยฉับพลัน การกำจัด * ยังกำจัดวงเล็บที่ทำให้สับสน (และพวกเขาบอกว่าการล้อเลียนทำให้เข้าใจง่ายขึ้น - ฮ่า!) การใช้งานของคุณยังเหมือนเดิม:

old_handler = signal(SIGHUP, new_handler);

แต่ตอนนี้คุณมีโอกาสในการทำความเข้าใจเกี่ยวกับการประกาศสำหรับold_handler, new_handlerและแม้กระทั่งsignalเมื่อคุณพบพวกเขาหรือจำเป็นต้องเขียนให้พวกเขา

ข้อสรุป

ปรากฎว่าโปรแกรมเมอร์ซีน้อยมากมีความสามารถในการคิดค้นคำประกาศที่ถูกต้องสำหรับสิ่งเหล่านี้ด้วยตัวเองโดยไม่ต้องปรึกษาเอกสารอ้างอิง

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

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


1
contrived ยากที่จะทำตาม ... +1 สำหรับความพยายามที่แม้ในขณะที่มันแสดงให้เห็นถึงจุดที่บางครั้งก็ยากที่จะได้รับสิทธิว่าในซี
celavek

4

ฉันคิดว่าคุณพลาดจุดนี้ไปบ้างเมื่อคุณมุ่งเน้นไปที่บิตจากซ้ายไปขวา

ปัญหาของ C และ C ++ เป็นไวยากรณ์ที่น่ากลัวที่พวกเขามีซึ่งยากที่จะอ่าน (คน) และการแยกวิเคราะห์ (เครื่องมือ)

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

ดังนั้นคุณอาจจับมันเมื่อมุ่งเน้นไปที่การอ่านและการแยก ... และนั่นเป็นเรื่องใหญ่ :)


นั่นเป็นเรื่องประหลาดใจอย่างมากเกี่ยวกับ Eclipse ที่ยังไม่สามารถแยกวิเคราะห์สิ่งต่าง ๆ เช่นการประกาศข้างต้น ทำไมพวกเขาไม่ใช้ parser C จริงเช่นจากgcc?
tchrist

@tchrist: ฉันตรวจพบปัญหาสองอย่างเกี่ยวกับ Eclipse และทั้งคู่ดูเหมือนว่าเชื่อมโยงกับมาโคร อาจเป็นปัญหาของตัวประมวลผลล่วงหน้ามากกว่าการสร้าง AST
Matthieu M.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.