ประโยชน์ของการให้ผู้ดำเนินการมอบหมายคืนค่าคืออะไร?


27

ฉันกำลังพัฒนาภาษาที่ฉันต้องการแทนที่ทั้ง Javascript และ PHP (ฉันไม่เห็นปัญหาใด ๆ กับสิ่งนี้มันไม่เหมือนภาษาใดภาษาหนึ่งเหล่านี้ที่มีฐานการติดตั้งขนาดใหญ่)

หนึ่งในสิ่งที่ฉันต้องการเปลี่ยนคือการเปลี่ยนผู้ประกอบการมอบหมายให้เป็นคำสั่งการมอบหมายลบความสามารถในการใช้ประโยชน์จากค่าที่ส่งคืน

x=1;          /* Assignment. */
if (x==1) {}  /* Comparison. */
x==1;         /* Error or warning, I've not decided yet. */
if (x=1) {}   /* Error. */

ฉันรู้ว่านี่จะหมายความว่าฟังก์ชั่นหนึ่งบรรทัดที่คน C รักมากจะไม่ทำงานอีกต่อไป ฉันคิด (มีหลักฐานน้อยกว่าประสบการณ์ส่วนตัวของฉัน) ว่าส่วนใหญ่ครั้งนี้เกิดขึ้นมันตั้งใจจริง ๆ ที่จะดำเนินการเปรียบเทียบ

หรือมันคืออะไร? มีการใช้งานจริงของค่าส่งคืนของผู้ดำเนินการที่ไม่สามารถเขียนใหม่ได้หรือไม่? (สำหรับภาษาใด ๆ ที่มีแนวคิดดังกล่าว)


12
JS และ PHP ไม่มี "ฐานการติดตั้ง" ที่มีขนาดใหญ่?
mhr

47
@mri ฉันสงสัยว่าการถากถาง
Andy Hunt

12
while((x = getValue()) != null) {}กรณีที่มีประโยชน์เดียวที่ฉันจะจำไว้คือ การเปลี่ยนจะเป็นสิ่งที่ไม่ดีนักเนื่องจากคุณจะต้องใช้งานbreakหรือทำซ้ำการx = getValueมอบหมาย
CodesInChaos

12
@ mri Ooh ไม่ฉันได้ยินว่าสองภาษานั้นเป็นเพียงเรื่องเล็กน้อยโดยไม่ต้องลงทุนอย่างมีนัยสำคัญเลย เมื่อไม่กี่คนที่ยืนยันการใช้ JS เห็นภาษาของฉันพวกเขาจะเปลี่ยนไปใช้ของฉันและไม่ต้องเขียน === อีกครั้ง ฉันมั่นใจว่าผู้ผลิตเบราว์เซอร์จะเปิดตัวการอัปเดตที่รวมภาษาของฉันไว้ข้างๆ JS ทันที :)
billpg

4
ฉันอยากจะแนะนำให้คุณทราบว่าหากคุณต้องการพัฒนาภาษาที่มีอยู่และคุณตั้งใจจะให้มีการนำไปใช้อย่างกว้างขวางความเข้ากันได้ย้อนหลัง 100% กับภาษาที่มีอยู่นั้นดี ดู TypeScript เป็นแบบอย่าง หากความตั้งใจของคุณคือการมอบทางเลือกที่ดีกว่าให้กับภาษาที่มีอยู่นั่นหมายความว่าคุณมีปัญหาที่หนักกว่ามาก ภาษาใหม่ที่มีการแก้ปัญหาที่เกิดขึ้นจริงที่มีอยู่มากมากดีกว่าภาษาที่มีอยู่เพื่อที่จะจ่ายสำหรับค่าใช้จ่ายในการเปลี่ยน การเรียนรู้ภาษาเป็นการลงทุนและจำเป็นต้องจ่าย
Eric Lippert

คำตอบ:


25

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

ดูภาษาที่มีอยู่ซึ่งอาจจะคุ้มค่า

  • Java และ C # ให้การแสดงออก แต่กำจัดข้อผิดพลาดที่คุณพูดถึงโดยกำหนดเงื่อนไขเพื่อประเมินเป็นบูลีน นี้ส่วนใหญ่ดูเหมือนว่าจะทำงานได้ดี แต่บางครั้งคนบ่นว่าไม่อนุญาตให้เงื่อนไขนี้ชอบif (x)ในสถานที่if (x != null)หรือขึ้นอยู่กับชนิดของif (x != 0)x
  • Python กำหนดการที่เหมาะสมแทนการแสดงออก ข้อเสนอแนะสำหรับการเปลี่ยนสิ่งนี้เป็นครั้งคราวถึงรายการจดหมายของ Python แต่ความประทับใจส่วนตัวของฉันคือสิ่งนี้เกิดขึ้นน้อยลงและสร้างเสียงรบกวนน้อยลงในแต่ละครั้งเมื่อเทียบกับคุณสมบัติ "หายไป" อื่น ๆ เช่น do-while loops, switch statement เป็นต้น

a = b = cแต่งูหลามช่วยกรณีพิเศษหนึ่งมอบหมายให้หลายชื่อในครั้งเดียว: นี่ถือว่าเป็นข้อความที่เทียบเท่ากับb = c; a = bและมันถูกใช้เป็นครั้งคราวดังนั้นมันอาจคุ้มค่าที่จะเพิ่มให้กับภาษาของคุณเช่นกัน (แต่ฉันไม่อยากเหงื่อเลย


5
+1 สำหรับการนำขึ้นa = b = cซึ่งคำตอบอื่น ๆ ไม่นำขึ้นมาจริงๆ
Leo

6
ความละเอียดที่สามคือการใช้สัญลักษณ์ที่แตกต่างกันสำหรับการกำหนด ปาสกาลใช้:=สำหรับการมอบหมาย
Brian

5
@Brian: แน่นอนเช่นเดียวกับ C # =คือการมอบหมาย==คือการเปรียบเทียบ
Marjan Venema

3
ใน C # บางอย่างที่คล้ายกันif (a = true)จะส่งสัญญาณเตือนC4706 ( The test value in a conditional expression was the result of an assignment.) GCC กับ C warning: suggest parentheses around assignment used as truth value [-Wparentheses]เช่นเดียวกันจะโยน คำเตือนเหล่านั้นสามารถปิดเสียงได้ด้วยวงเล็บพิเศษ แต่พวกเขาอยู่ที่นั่นเพื่อสนับสนุนอย่างชัดเจนว่าการมอบหมายนั้นเป็นการจงใจ
บ๊อบ

2
@delnan เพียงแค่แสดงความคิดเห็นค่อนข้างทั่วไป แต่มันก็ถูกจุดประกายโดย "ลบหลุมพรางที่คุณพูดถึงโดยกำหนดเงื่อนไขในการประเมินการบูลีน" - a = true ไม่ประเมินบูลีนและดังนั้นจึงไม่ผิดพลาด แต่ก็ยังยกคำเตือนที่เกี่ยวข้องใน C #
บ๊อบ

11

มีการใช้งานจริงของค่าส่งคืนของผู้ดำเนินการที่ไม่สามารถเขียนใหม่ได้หรือไม่?

โดยทั่วไปแล้วไม่มี แนวคิดของการมีค่าของการแสดงออกที่ได้รับมอบหมายเป็นค่าที่ได้รับมอบหมายหมายความว่าเรามีการแสดงออกซึ่งอาจจะใช้สำหรับทั้งผลข้างเคียงและมูลค่าของมันและที่หลายคนคิดว่าจะทำให้เกิดความสับสน

โดยทั่วไปประเพณีมักจะทำให้การแสดงออกมีขนาดกะทัดรัด:

x = y = z;

มีซีแมนทิกส์ใน C # ของ "แปลง z เป็นชนิดของ y, กำหนดค่าที่แปลงแล้วเป็น y, ค่าที่แปลงแล้วคือค่าของนิพจน์, แปลงให้เป็นประเภทของ x, กำหนดเป็น x"

แต่เราอยู่ในขอบเขตของผลข้างเคียงที่จำเป็นในบริบทของคำสั่งดังนั้นจึงมีประโยชน์ที่น่าสนใจน้อยมาก

y = z;
x = y;

ในทำนองเดียวกันกับ

M(x = 123);

การจดชวเลข

x = 123;
M(x);

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

ฉันกำลังพัฒนาภาษาที่ฉันต้องการแทนที่ทั้ง Javascript และ PHP

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

let x be 1;

สีแดงนั้น. หรือ

x <-- 1;

หรือดีกว่า:

1 --> x;

หรือยังดีกว่า

1 → x;

อย่างมีวิธีการที่ใด ๆ x == 1ของผู้กำลังจะสับสนกับการไม่ได้


1
โลกพร้อมสำหรับสัญลักษณ์ที่ไม่ใช่ ASCII Unicode ในภาษาการเขียนโปรแกรมหรือไม่?
billpg

เท่าที่ฉันจะรักสิ่งที่คุณแนะนำหนึ่งในเป้าหมายของฉันคือ JavaScript ส่วนใหญ่ "ดีเขียน" สามารถ ported กับการเปลี่ยนแปลงเล็กน้อยหรือไม่มีเลย
billpg

2
@billpg: โลกพร้อมหรือยัง? ฉันไม่รู้ - โลกพร้อมสำหรับ APL ในปี 1964 หลายสิบปีก่อนการประดิษฐ์ Unicode หรือไม่? นี่คือโปรแกรมใน APL ที่เลือกสุ่มเปลี่ยนตัวเลขหกตัวจาก 40 ตัวแรก: x[⍋x←6?40] APL ต้องการแป้นพิมพ์พิเศษของตัวเอง แต่มันเป็นภาษาที่ประสบความสำเร็จ
Eric Lippert

@billpg: Workshop ของโปรแกรมเมอร์ Macintosh ใช้สัญลักษณ์ที่ไม่ใช่ ASCII สำหรับสิ่งต่าง ๆ เช่นแท็ก regex หรือการเปลี่ยนเส้นทางของ stderr ในทางกลับกัน MPW มีข้อได้เปรียบที่ Macintosh ทำให้การพิมพ์อักขระที่ไม่ใช่ ASCII ทำได้ง่าย ฉันต้องสารภาพปริศนาบางอย่างว่าทำไมไดรเวอร์แป้นพิมพ์ของสหรัฐอเมริกาไม่มีวิธีการที่ดีในการพิมพ์อักขระที่ไม่ใช่ ASCII การป้อนหมายเลข Alt ไม่เพียง แต่ต้องค้นหารหัสตัวอักษร - ในแอปพลิเคชั่นหลายตัวที่ไม่สามารถใช้งานได้
supercat

หืมทำไมเราถึงเลือกที่จะ "ชอบ" a+b*c --> xใช่ไหม มันดูแปลกสำหรับฉัน
Ruslan

9

หลายภาษาเลือกเส้นทางในการกำหนดคำสั่งแทนนิพจน์รวมถึง Python:

foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg

และ Golang:

var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies

ภาษาอื่น ๆ ไม่มีการบ้าน แต่ค่อนข้างจะถูก จำกัด ขอบเขตเช่น OCaml:

let foo = 42 in
  if foo = 42 then
    print_string "hi"

อย่างไรก็ตามletเป็นการแสดงออกของตัวเอง

ข้อดีของการอนุญาตให้ทำงานคือเราสามารถตรวจสอบค่าส่งคืนของฟังก์ชั่นภายในเงื่อนไขได้โดยตรงเช่นในตัวอย่างของ Perl นี้:

if (my $result = some_computation()) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}

Perl ยังกำหนดขอบเขตการประกาศเงื่อนไขเท่านั้นซึ่งทำให้มีประโยชน์มาก นอกจากนี้ยังจะเตือนถ้าคุณกำหนดภายในเงื่อนไขโดยไม่ต้องประกาศตัวแปรใหม่ที่นั่น - if ($foo = $bar)จะเตือนif (my $foo = $bar)จะไม่

การกำหนดในคำสั่งอื่นมักจะเพียงพอ แต่สามารถนำปัญหาการกำหนดขอบเขต:

my $result = some_computation()
if ($result) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}
# $result is still visible here - eek!

Golang พึ่งพาค่าส่งคืนอย่างมากสำหรับการตรวจสอบข้อผิดพลาด ดังนั้นจึงอนุญาตให้เงื่อนไขที่จะใช้คำสั่งการเริ่มต้น:

if result, err := some_computation(); err != nil {
  fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)

ภาษาอื่นใช้ระบบชนิดเพื่อไม่อนุญาตนิพจน์ที่ไม่ใช่บูลีนภายในเงื่อนไข:

int foo;
if (foo = bar()) // Java does not like this

แน่นอนว่าล้มเหลวเมื่อใช้ฟังก์ชันที่คืนค่าบูลีน

ขณะนี้เราได้เห็นกลไกต่าง ๆ เพื่อป้องกันการมอบหมายโดยไม่ได้ตั้งใจ:

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

ฉันได้จัดอันดับพวกเขาตามลำดับจากน้อยไปมาก - การมอบหมายภายในนิพจน์อาจมีประโยชน์ (และเป็นเรื่องง่ายที่จะหลีกเลี่ยงปัญหาของ Python โดยมีไวยากรณ์การประกาศที่ชัดเจนและไวยากรณ์อาร์กิวเมนต์ที่มีชื่อต่างกัน) แต่มันก็โอเคที่จะไม่อนุญาตเนื่องจากมีตัวเลือกอื่น ๆ มากมายที่ให้ผลเหมือนกัน

รหัสที่ไม่มีข้อผิดพลาดสำคัญกว่ารหัสตัวอักษร


+1 สำหรับ "ไม่อนุญาตการมอบหมายเป็นนิพจน์" การใช้งานกรณีสำหรับการมอบหมายงานเป็นนิพจน์ไม่ได้พิสูจน์ความเป็นไปได้ของข้อบกพร่องและปัญหาการอ่านได้
โผล่

7

คุณพูดว่า "ฉันคิด (มีหลักฐานน้อยกว่าประสบการณ์ส่วนตัวของฉัน) ว่าส่วนใหญ่ครั้งนี้เกิดขึ้นมันตั้งใจจริง ๆ ที่จะทำการเปรียบเทียบ"

ทำไมไม่แก้ไขปัญหา?

แทนที่จะ = สำหรับการมอบหมายและ == สำหรับการทดสอบความเท่ากันทำไมไม่ใช้: = สำหรับการมอบหมายและ = (หรือแม้กระทั่ง ==) เพื่อความเท่าเทียมกัน?

สังเกต:

if (a=foo(bar)) {}  // obviously equality
if (a := foo(bar)) { do something with a } // obviously assignment

หากคุณต้องการทำให้โปรแกรมเมอร์ยากต่อการกำหนดความผิดพลาดให้เท่าเทียมกันยิ่งขึ้น

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

int a = some_value();
if (a) {}

คุณบังคับให้โปรแกรมเมอร์เขียน:

int a = some_value();
if (a /= 0) {} // Note that /= means 'not equal'.  This is your Ada lesson for today.

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


2
(1) :=สำหรับการมอบหมายและ=เพื่อความเท่าเทียมกันอาจแก้ไขปัญหานี้ได้ แต่ด้วยค่าใช้จ่ายของการทำให้แปลกแยกโปรแกรมเมอร์ทุกคนที่ไม่ได้เติบโตขึ้นโดยใช้ภาษาที่ไม่สำคัญ (2) ประเภทอื่นนอกเหนือจาก bools ที่อนุญาตในเงื่อนไขไม่ได้เกิดจากการผสม bools และจำนวนเต็มมันก็เพียงพอแล้วที่จะให้การตีความที่ถูกต้อง / ผิดประเภทอื่น ๆ ภาษาที่ใหม่กว่าที่ไม่กลัวที่จะเบี่ยงเบนจาก C ได้ทำไปแล้วสำหรับประเภทอื่น ๆ ที่ไม่ใช่จำนวนเต็ม (เช่น Python พิจารณาว่าคอลเลกชันว่างเปล่าเป็นเท็จ)

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

1
@delnan: นักปราชญ์ครั้งหนึ่งเคยพูดว่า "ทำให้มันง่ายที่สุดเท่าที่จะทำได้ หากวัตถุประสงค์ของคุณคือกำจัดข้อผิดพลาดส่วนใหญ่ของ a = b vs. a == b ให้ จำกัด โดเมนของการทดสอบตามเงื่อนไขเป็นบูลีนและกำจัดกฎการแปลงประเภทเริ่มต้นสำหรับบูลีน <other> -> ทำให้คุณเกือบหมดทุกอย่าง ที่นั่น ณ จุดนั้นถ้า (a = b) {} มีความถูกต้องทางกฎหมายเฉพาะถ้า a และ b เป็นบูลีนและ a เป็นค่าทางกฎหมาย
John R. Strohm

ทำให้ได้รับมอบหมายคำสั่งเป็นอย่างน้อยเป็นง่ายๆเป็น - เนื้อหาได้ง่ายกว่า - การเปลี่ยนแปลงที่คุณเสนอและประสบความสำเร็จในอย่างน้อยเท่า - เนื้อหามากยิ่งขึ้น (ไม่ได้อนุญาตif (a = b)สำหรับ lvalue ที่บูล A, B) ในภาษาที่ไม่มีการพิมพ์แบบคงที่มันจะให้ข้อความแสดงข้อผิดพลาดที่ดีกว่ามาก (ในเวลาในการแยกวิเคราะห์และเวลาทำงาน) นอกจากนี้การป้องกัน "a = b vs. a == b error" อาจไม่ใช่วัตถุประสงค์ที่เกี่ยวข้องเท่านั้น ตัวอย่างเช่นฉันต้องการอนุญาตให้รหัสif items:หมายถึงค่าเฉลี่ยif len(items) != 0และฉันต้องยอมแพ้เพื่อ จำกัด เงื่อนไขสำหรับบูลีน

1
@delnan Pascal เป็นภาษาที่ไม่สำคัญหรือไม่? ผู้คนหลายล้านคนเรียนรู้การเขียนโปรแกรมโดยใช้ Pascal (และ / หรือ Modula ซึ่งเกิดจาก Pascal) และ Delphi ยังคงใช้กันทั่วไปในหลายประเทศ (อาจจะไม่มากในของคุณ)
jwenting

5

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

ตัวอย่างเช่นใน Java:

while ((Object ob = x.next()) != null) {
    // This will loop through calling next() until it returns null
    // The value of the returned object is available as ob within the loop
}

ทางเลือกโดยไม่ต้องใช้การกำหนดให้ฝังต้องobกำหนดไว้นอกขอบเขตของการวนซ้ำและสองตำแหน่งรหัสแยกต่างหากที่เรียก x.next ()

มีการกล่าวถึงแล้วว่าคุณสามารถกำหนดตัวแปรหลายตัวในขั้นตอนเดียว

x = y = z = 3;

สิ่งนี้เป็นสิ่งที่ใช้กันมากที่สุด แต่โปรแกรมเมอร์ที่สร้างสรรค์มักจะเกิดขึ้นมากมาย


ในขณะที่เงื่อนไขลูปจะยกเลิกการจัดสรรและสร้างobวัตถุใหม่กับทุกลูปหรือไม่
user3932000

@ user3932000 ในกรณีนี้อาจไม่ปกติ x.next () กำลังวนซ้ำบางสิ่ง เป็นไปได้อย่างแน่นอนว่ามันจะทำได้
ทิม B

1

เมื่อคุณทำตามกฎทั้งหมดทำไมจึงอนุญาตให้การมอบหมายเปลี่ยนค่าและไม่อนุญาตการมอบหมายภายในขั้นตอนแบบมีเงื่อนไข สิ่งนี้จะช่วยให้คุณได้น้ำตาล syntactic เพื่อเริ่มต้นได้ง่ายในขณะที่ยังคงป้องกันข้อผิดพลาดในการเขียนโค้ดทั่วไป

กล่าวอีกนัยหนึ่งทำให้กฎหมายนี้:

a=b=c=0;

แต่ทำให้สิ่งนี้ผิดกฎหมาย:

if (a=b) ...

2
ดูเหมือนเป็นกฎที่ค่อนข้างจะเป็นโฆษณา การกำหนดคำแถลงและขยายคำสั่งเพื่อให้a = b = cดูเหมือนว่าเป็นมุมฉากมากขึ้นและนำไปปฏิบัติได้ง่ายขึ้นเช่นกัน วิธีการทั้งสองนี้ไม่เห็นด้วยกับการมอบหมายในนิพจน์ ( a + (b = c)) แต่คุณยังไม่ได้เข้าร่วมดังนั้นฉันคิดว่าพวกเขาไม่สำคัญ

"ใช้งานง่าย" ไม่ควรคำนึงถึงมากนัก คุณกำลังกำหนดส่วนติดต่อผู้ใช้ - ใส่ความต้องการของผู้ใช้ก่อน คุณเพียงแค่ต้องถามตัวเองว่าพฤติกรรมนี้ช่วยหรือขัดขวางผู้ใช้
Bryan Oakley

หากคุณไม่อนุญาตให้มีการแปลงเป็น Bool โดยปริยายคุณไม่ต้องกังวลกับการกำหนดเงื่อนไข
ratchet freak

การใช้งานง่ายขึ้นเป็นเพียงหนึ่งในข้อโต้แย้งของฉัน แล้วที่เหลือล่ะ จากมุม UI ฉันอาจเพิ่มการออกแบบ IMO ที่ไม่ต่อเนื่องกันและข้อยกเว้นแบบเฉพาะกิจโดยทั่วไปจะเป็นอุปสรรคต่อผู้ใช้ในการติดตามและแก้ไขกฎภายใน

@ratchetfreak คุณอาจยังมีปัญหากับการกำหนด bools จริง
jk

0

ด้วยเสียงของมันคุณกำลังอยู่บนเส้นทางของการสร้างภาษาที่ค่อนข้างเข้มงวด

เมื่อคำนึงถึงสิ่งนั้นบังคับให้ผู้คนเขียน:

a=c;
b=c;

แทน:

a=b=c;

อาจดูเหมือนการปรับปรุงเพื่อป้องกันไม่ให้คนทำ:

if (a=b) {

เมื่อพวกเขาตั้งใจจะทำ:

if (a==b) {

แต่ในที่สุดข้อผิดพลาดประเภทนี้จะตรวจจับและเตือนได้ง่ายว่าเป็นรหัสทางกฎหมายหรือไม่

อย่างไรก็ตามมีสถานการณ์ที่กำลังทำ:

a=c;
b=c;

ไม่ได้หมายความว่า

if (a==b) {

จะเป็นจริง

ถ้า c เป็นฟังก์ชัน c () จริง ๆ แล้วมันจะได้ผลลัพธ์ที่แตกต่างกันในแต่ละครั้งที่เรียกใช้ (มันอาจจะแพงด้วยเช่นกัน ... )

ในทำนองเดียวกันถ้า c เป็นตัวชี้ไปยังฮาร์ดแวร์ที่แมปหน่วยความจำแล้ว

a=*c;
b=*c;

ทั้งสองมีแนวโน้มที่จะแตกต่างกันและอาจมีผลกระทบทางอิเล็กทรอนิกส์ต่อฮาร์ดแวร์ในการอ่านแต่ละครั้ง

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


4
เพื่อเทียบเท่าa = b = cไม่ได้ก็a = c; b = c b = c; a = bหลีกเลี่ยงการทำซ้ำของผลข้างเคียงและยังคงการแก้ไขaและbในลำดับเดียวกัน นอกจากนี้ข้อโต้แย้งที่เกี่ยวข้องกับฮาร์ดแวร์ทั้งหมดเป็นประเภทที่โง่: ภาษาส่วนใหญ่ไม่ใช่ภาษาของระบบและไม่ได้ออกแบบมาเพื่อแก้ไขปัญหาเหล่านี้และไม่ได้ใช้ในสถานการณ์ที่เกิดปัญหาเหล่านี้ สิ่งนี้จะเพิ่มเป็นสองเท่าสำหรับภาษาที่พยายามแทนที่ JavaScript และ / หรือ PHP

delnan ปัญหาไม่ได้เป็นตัวอย่างที่ประดิษฐ์ขึ้นมา จุดยังคงยืนอยู่ว่าพวกเขาแสดงประเภทของสถานที่ที่การเขียน a = b = c เป็นเรื่องปกติและในกรณีฮาร์ดแวร์ถือว่าเป็นแนวปฏิบัติที่ดีตามที่ OP ขอ ฉันแน่ใจว่าพวกเขาจะสามารถพิจารณาความเกี่ยวข้องของพวกเขากับสภาพแวดล้อมที่คาดหวัง
Michael Shaw

เมื่อมองย้อนกลับไปปัญหาของฉันกับคำตอบนี้ไม่ได้มุ่งเน้นไปที่กรณีการเขียนโปรแกรมระบบเป็นหลัก (แม้ว่ามันจะไม่ดีเท่าที่เขียนไว้) แต่มันก็ขึ้นอยู่กับการเขียนผิดพลาด ตัวอย่างไม่ใช่ตัวอย่างของสถานที่ที่a=b=cเป็นเรื่องปกติ / มีประโยชน์พวกเขาเป็นตัวอย่างของสถานที่ที่ต้องสั่งซื้อและจำนวนของผลข้างเคียง นั่นเป็นอิสระอย่างสิ้นเชิง เขียนซ้ำการมอบหมายที่ถูกต้องและตัวแปรทั้งสองมีความถูกต้องเท่ากัน

@delnan: ค่า rvalue จะถูกแปลงเป็นประเภทของbอุณหภูมิหนึ่งและจะถูกแปลงเป็นประเภทของaอุณหภูมิอื่น ระยะเวลาสัมพัทธ์ของเมื่อค่าเหล่านั้นถูกจัดเก็บจริงไม่ระบุ จากมุมมองการออกแบบภาษาฉันคิดว่ามันสมเหตุสมผลที่จะกำหนดให้ค่า lvalues ​​ทั้งหมดในคำสั่งที่มีหลายการมอบหมายมีประเภทการจับคู่และอาจจำเป็นต้องใช้และไม่มีความผันผวนใด ๆ
supercat

0

ประโยชน์ที่ยิ่งใหญ่ที่สุดในใจของฉันที่ได้รับมอบหมายคือการแสดงออกที่จะช่วยให้ไวยากรณ์ของคุณจะง่ายขึ้นถ้าหนึ่งในเป้าหมายของคุณคือ "ทุกอย่างคือการแสดงออก" - เป้าหมายของ LISP โดยเฉพาะ

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

วิธีหนึ่งที่จะช่วยให้การกำหนดหรือมากกว่าผลกระทบของงานที่จะแสดงออกโดยไม่ต้องแนะนำที่มีศักยภาพสำหรับif(x=1)การเกิดอุบัติเหตุที่มี C คือการใช้เสียงกระเพื่อมเหมือนletสร้างเช่นซึ่งอาจในภาษาของคุณประเมินเป็น(let ((x 2) (y 3)) (+ x y)) 5การใช้letวิธีนี้ไม่จำเป็นต้องมีการมอบหมายทางเทคนิคเลยในภาษาของคุณหากคุณกำหนดletว่าเป็นการสร้างขอบเขตศัพท์ กำหนดด้วยวิธีการletสร้างสามารถรวบรวมได้เช่นเดียวกับการสร้างและการเรียกฟังก์ชั่นการปิดซ้อนกับข้อโต้แย้ง

ในทางกลับกันถ้าคุณกังวลกับif(x=1)กรณีนี้ แต่ต้องการให้การมอบหมายเป็นนิพจน์เหมือนกับใน C บางทีการเลือกโทเค็นที่แตกต่างกันก็เพียงพอแล้ว ที่ได้รับมอบหมาย: หรือx := 1 x <- 1การเปรียบเทียบ: x == 1. x = 1ไวยากรณ์ผิดพลาด:


1
letแตกต่างจากการมอบหมายในหลายวิธีมากกว่าการแนะนำตัวแปรใหม่ในขอบเขตใหม่ สำหรับ starters นั้นจะไม่มีผลกับโค้ดภายนอกletเนื้อความของดังนั้นจึงต้องมีการซ้อนโค้ดทั้งหมด (สิ่งที่ควรใช้ตัวแปร) เพิ่มเติมซึ่งเป็นข้อเสียที่สำคัญในการกำหนดรหัสหนัก หากมีใครที่จะไปตามเส้นทางนั้นset!จะเป็นอะนาล็อก Lisp ที่ดีกว่าซึ่งแตกต่างจากการเปรียบเทียบอย่างสมบูรณ์ แต่ไม่ต้องการรังหรือขอบเขตใหม่

@delnan: ฉันต้องการดูชุดค่าผสมประกาศและกำหนดไวยากรณ์ที่จะห้ามการกำหนดใหม่ แต่จะอนุญาตให้เผยแพร่ซ้ำภายใต้กฎที่ (1) การประกาศซ้ำจะถูกกฎหมายสำหรับตัวระบุประกาศและกำหนดเท่านั้นและ (2 ) การประกาศซ้ำจะ "ยกเลิกการประกาศ" ตัวแปรในขอบเขตที่ล้อมรอบทั้งหมด ดังนั้นมูลค่าของตัวระบุที่ถูกต้องจะเป็นสิ่งที่ได้รับมอบหมายในการประกาศชื่อก่อนหน้านี้ ซึ่งอาจดูดีกว่าการเพิ่มบล็อกการกำหนดขอบเขตสำหรับตัวแปรที่ใช้สำหรับสองสามบรรทัดเท่านั้นหรือต้องกำหนดชื่อใหม่ให้กับตัวแปรชั่วคราวแต่ละตัว
supercat

0

ฉันรู้ว่านี่จะหมายความว่าฟังก์ชั่นหนึ่งบรรทัดที่คน C รักมากจะไม่ทำงานอีกต่อไป ฉันคิด (มีหลักฐานน้อยกว่าประสบการณ์ส่วนตัวของฉัน) ว่าส่วนใหญ่ครั้งนี้เกิดขึ้นมันตั้งใจจริง ๆ ที่จะดำเนินการเปรียบเทียบ

จริง นี่คือไม่มีอะไรใหม่ชุดย่อยที่ปลอดภัยของภาษา C ทั้งหมดได้ทำข้อสรุปนี้แล้ว

MISRA-C, CERT-Cและอื่น ๆ ทั้งหมดที่ได้รับมอบหมายห้ามอยู่ในเงื่อนไขเพียงเพราะมันเป็นอันตราย

ไม่มีกรณีที่รหัสที่ขึ้นอยู่กับการมอบหมายภายในเงื่อนไขไม่สามารถเขียนใหม่ได้


นอกจากนี้มาตรฐานดังกล่าวยังเตือนไม่ให้มีการเขียนรหัสที่ต้องอาศัยลำดับของการประเมินด้วย การมอบหมายหลายอย่างในแถวเดียวx=y=z;เป็นกรณีนี้ หากแถวที่มีการกำหนดหลายรายการมีผลข้างเคียง (การเรียกใช้ฟังก์ชั่นการเข้าถึงตัวแปรระเหย ฯลฯ ) คุณจะไม่สามารถทราบได้ว่าผลข้างเคียงใดที่จะเกิดขึ้นก่อน

ไม่มีคะแนนตามลำดับระหว่างการประเมินผลตัวถูกดำเนินการ ดังนั้นเราจึงไม่สามารถทราบได้ว่า subexpression yได้รับการประเมินก่อนหรือหลังz: มันเป็นพฤติกรรมที่ไม่ระบุใน C. ดังนั้นรหัสดังกล่าวอาจไม่น่าเชื่อถือไม่พกพาได้และไม่สอดคล้องกับชุดย่อยที่ปลอดภัยของ C ที่กล่าวถึง

y=z; x=y;การแก้ปัญหาจะได้รับการแทนที่โค้ดด้วย สิ่งนี้จะเพิ่มจุดลำดับและรับประกันลำดับของการประเมิน


จากปัญหาทั้งหมดที่เกิดขึ้นในภาษา C ดังนั้นภาษาสมัยใหม่ใด ๆ ก็สามารถทำได้ดีกับการห้ามการบ้านในสภาพเช่นเดียวกับการมอบหมายหลายอย่างในแถวเดียว

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