ทำไม x == (x = y) ไม่เหมือนกับ (x = y) == x


207

ลองพิจารณาตัวอย่างต่อไปนี้:

class Quirky {
    public static void main(String[] args) {
        int x = 1;
        int y = 3;

        System.out.println(x == (x = y)); // false
        x = 1; // reset
        System.out.println((x = y) == x); // true
     }
}

ฉันไม่แน่ใจว่ามีรายการใน Java Language Specification ที่สั่งให้โหลดค่าก่อนหน้าของตัวแปรเพื่อเปรียบเทียบกับด้านขวา (x = y ) ซึ่งควรจะคำนวณตามลำดับโดยนัยโดยวงเล็บ

ทำไมนิพจน์แรกถึงประเมินfalseแต่อันดับที่สองประเมินได้trueอย่างไร ผมคาดว่าจะมี(x = y)การประเมินครั้งแรกและจากนั้นก็จะเปรียบเทียบxกับตัวเอง ( 3) trueและผลตอบแทน


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

int oldX = x;
x = y;
return oldX == y;

ซึ่งง่ายกว่าคำสั่ง x86 CMPXCHG ซึ่งสมควรได้รับนิพจน์ที่สั้นกว่าใน Java


62
ด้านซ้ายมือจะถูกประเมินเสมอก่อนทางด้านขวา วงเล็บไม่ได้สร้างความแตกต่างให้กับที่
Louis Wasserman

11
การประเมินการแสดงออกx = yมีความเกี่ยวข้องอย่างแน่นอนและทำให้เกิดผลข้างเคียงที่มีการตั้งค่าของx y
Louis Wasserman

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

50
คำถามจริงคือทำไมคุณต้องการเขียนโค้ดเช่นนี้
klutt

26
กุญแจสำคัญในคำถามของคุณคือความเชื่อผิด ๆ ของคุณที่วงเล็บแสดงถึงลำดับการประเมินผล นั่นเป็นความเชื่อร่วมกันเพราะเราสอนวิชาคณิตศาสตร์ในโรงเรียนประถมศึกษาและเพราะหนังสือเริ่มต้นบางเล่มยังเข้าใจผิดแต่เป็นความเชื่อที่ผิด นี่เป็นคำถามที่ค่อนข้างบ่อย คุณอาจได้รับประโยชน์จากการอ่านบทความของฉันในเรื่อง; มันเกี่ยวกับ C # แต่ใช้กับ Java: ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order ericlippert.com/2009/08/10/precedence-vs-order-redux
Eric Lippert

คำตอบ:


97

ซึ่งตามคำสั่งโดยนัยโดยวงเล็บควรคำนวณก่อน

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

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

เมื่อเสร็จแล้ว (เช่นเมื่อรหัสของคุณได้รับการแยกวิเคราะห์ในโปรแกรม) ตัวถูกดำเนินการเหล่านั้นยังต้องได้รับการประเมินและมีกฎแยกต่างหากเกี่ยวกับวิธีการทำ: กฎกล่าว (ตามที่ Andrew ได้แสดงให้เราเห็น) ระบุว่า LHS ของแต่ละการดำเนินการ ถูกประเมินเป็นอันดับแรกใน Java

โปรดทราบว่านี่ไม่ใช่กรณีในทุกภาษา ตัวอย่างเช่นใน C ++ ยกเว้นว่าคุณกำลังใช้ตัวดำเนินการลัดวงจรเช่น&&หรือ||ลำดับการประเมินผลของตัวถูกดำเนินการนั้นไม่มีการระบุโดยทั่วไปและคุณไม่ควรใช้วิธีนี้

ครูต้องหยุดอธิบายความสำคัญของโอเปอเรเตอร์โดยใช้วลีที่ทำให้เข้าใจผิดเช่น "สิ่งนี้ทำให้การเติมเกิดขึ้นก่อน" ให้นิพจน์x * y + zคำอธิบายที่เหมาะสมคือ "ลำดับความสำคัญของโอเปอเรเตอร์ทำให้การเพิ่มเกิดขึ้นระหว่างx * yและzมากกว่าระหว่างyและz" โดยไม่กล่าวถึง "คำสั่ง" ใด ๆ


6
ฉันหวังว่าครูของฉันได้แยกบางส่วนระหว่างคณิตศาสตร์พื้นฐานและไวยากรณ์ที่พวกเขาเคยเป็นตัวแทนเช่นถ้าเราใช้เวลาหนึ่งวันกับตัวเลขโรมันหรือสัญลักษณ์โปแลนด์หรืออะไรก็ตามและเห็นว่าการเพิ่มนั้นมีคุณสมบัติเหมือนกัน เราเรียนรู้การเชื่อมโยงและคุณสมบัติทั้งหมดในโรงเรียนมัธยมดังนั้นจึงมีเวลาเหลือเฟือ
John P

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

5
@ JohnP: มันแย่ลง 5 * 4 หมายถึง 5 + 5 + 5 + 5 หรือ 4 + 4 + 4 + 4 + 4 หรือไม่? ครูบางคนยืนยันว่ามีเพียงหนึ่งในตัวเลือกที่ถูกต้อง
Brian

3
@Brian แต่ ... แต่ ... การคูณจำนวนจริงคือการสลับ!
การแข่งขัน Lightness ใน Orbit

2
ในโลกแห่งความคิดของฉันวงเล็บหนึ่งคู่หมายถึง "จำเป็นสำหรับ" การคำนวณ´a * (b + c) ´วงเล็บจะแสดงว่าผลลัพธ์ของการบวกนั้นจำเป็นสำหรับการคูณ การกำหนดค่าตามความชอบของตัวดำเนินการโดยนัยใด ๆ สามารถแสดงโดย parens ยกเว้นกฎ LHS-first หรือ RHS-first (นั่นเป็นความจริงหรือเปล่า?) @Brian ในทางคณิตศาสตร์มีบางกรณีที่หายากซึ่งการคูณสามารถแทนที่ด้วยการเติมซ้ำได้ แต่นั่นไม่จริงเสมอไป ดังนั้นการศึกษาของคุณควรจริงๆมีตากับสิ่งที่คนจะบอก ....
syck

164

==เป็นไบนารีประกอบความเสมอภาค

ซ้ายมือตัวถูกดำเนินการของผู้ประกอบการไบนารีดูเหมือนจะได้รับการประเมินอย่างเต็มที่ก่อนที่จะเป็นส่วนหนึ่งของการใด ๆทางด้านขวาถูกดำเนินการประเมิน

ข้อมูลจำเพาะ Java 11> ลำดับการประเมินผล> ประเมินตัวถูกดำเนินการทางด้านซ้ายก่อน


42
ถ้อยคำ "ดูเหมือนจะเป็น" ไม่ดูเหมือนพวกเขาแน่ใจ tbh
นาย Lister

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

24
@MrLister "ดูเหมือนจะเป็น" ดูเหมือนจะเป็นตัวเลือกคำที่ไม่ดีในส่วนของพวกเขา โดย "ปรากฏ" พวกเขาหมายถึง "ประจักษ์เป็นปรากฏการณ์สำหรับนักพัฒนา" "มีประสิทธิภาพ" อาจเป็นวลีที่ดีกว่า
เคลวิน

18
ในชุมชน C ++ สิ่งนี้จะเทียบเท่ากับกฎ "as-if" ... ตัวถูกดำเนินการจะต้องทำตัวเป็น "ราวกับว่า" มันถูกใช้งานตามกฎต่อไปนี้แม้ว่าในทางเทคนิคแล้วมันจะไม่
Michael Edenfield

2
@ เคลวินฉันเห็นด้วยฉันจะเลือกคำนั้นแทนที่จะเป็น "ดูเหมือนจะเป็น"
MC Emperor

149

ดังที่ LouisWasserman กล่าวว่านิพจน์จะถูกประเมินจากซ้ายไปขวา และจาวาไม่สนใจสิ่งที่ "ประเมิน" ทำจริง ๆ แล้วมันแค่สนใจเกี่ยวกับการสร้างคุณค่า (ไม่ลบเลือนสุดท้าย) เพื่อทำงานกับ

//the example values
x = 1;
y = 3;

ดังนั้นในการคำนวณผลลัพธ์แรกของSystem.out.println()การทำงาน

x == (x = y)
1 == (x = y)
1 == (x = 3) //assign 3 to x, returns 3
1 == 3
false

และเพื่อคำนวณวินาที:

(x = y) == x
(x = 3) == x //assign 3 to x, returns 3
3 == x
3 == 3
true

โปรดทราบว่าค่าที่สองจะประเมินเป็นจริงเสมอโดยไม่คำนึงถึงค่าเริ่มต้นของxและyเนื่องจากคุณกำลังเปรียบเทียบการกำหนดค่ากับตัวแปรที่ได้รับมอบหมายอย่างมีประสิทธิภาพa = bและbจะประเมินตามลำดับนั้นเสมอเหมือนกัน ตามคำจำกัดความ


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

25

ฉันไม่แน่ใจว่ามีรายการใน Java Language Specification ที่สั่งให้โหลดค่าก่อนหน้าของตัวแปร ...

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

... ทางด้านขวา(x = y)ซึ่งควรคำนวณจากคำสั่งโดยนัยสำหรับวงเล็บก่อน

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

ทำไมนิพจน์แรกประเมินว่าเป็นเท็จ แต่ข้อที่สองประเมินเป็นจริง

กฎสำหรับ==ผู้ปฏิบัติงานคือ: ประเมินทางด้านซ้ายเพื่อสร้างค่าประเมินทางด้านขวาเพื่อสร้างค่าเปรียบเทียบค่าการเปรียบเทียบคือค่าของนิพจน์

กล่าวอีกนัยหนึ่งความหมายของคำexpr1 == expr2นั้นเหมือนกับว่าคุณได้เขียนtemp1 = expr1; temp2 = expr2;และประเมินผลแล้วtemp1 == temp2และประเมินผลแล้ว

กฎสำหรับ=ผู้ประกอบการที่มีตัวแปรท้องถิ่นทางด้านซ้ายคือ: ประเมินทางด้านซ้ายเพื่อสร้างตัวแปรประเมินด้านขวาในการสร้างค่าดำเนินการกำหนดผลที่ได้คือค่าที่ได้รับมอบหมาย

ดังนั้นรวบรวม:

x == (x = y)

เรามีผู้ดำเนินการเปรียบเทียบ ประเมินทางด้านซ้ายเพื่อสร้างค่า - เราได้ค่าปัจจุบันxเป็น ประเมินทางด้านขวา: นั่นคือการกำหนดค่าดังนั้นเราจึงประเมินด้านซ้ายเพื่อสร้างตัวแปร - ตัวแปรx- เราประเมินด้านขวา - ค่าปัจจุบันของy- กำหนดให้xและผลลัพธ์คือค่าที่กำหนด จากนั้นเราจะเปรียบเทียบค่าดั้งเดิมของxกับค่าที่กำหนด

คุณสามารถ(x = y) == xออกกำลังกายได้ โปรดจำไว้ว่ากฎทั้งหมดสำหรับการประเมินด้านซ้ายเกิดขึ้นก่อนกฎการประเมินด้านขวาทั้งหมด

ฉันคาดว่าจะประเมิน (x = y) ก่อนแล้วจึงเปรียบเทียบ x กับตัวเอง (3) และคืนค่าจริง

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

คำถามนี้แตกต่างจาก "ลำดับการประเมินของนิพจน์ย่อยในนิพจน์ Java"

คำสั่งนี้เป็นเท็จ คำถามนั้นมีความสัมพันธ์กันอย่างสมบูรณ์

x ไม่ใช่ 'subexpression' ที่นี่แน่นอน

คำสั่งนี้ยังเป็นเท็จ มันเป็น subexpression สองครั้งในแต่ละตัวอย่าง

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

ฉันไม่รู้ว่ามันหมายถึงอะไร

เห็นได้ชัดว่าคุณยังมีความเชื่อที่ผิด ๆ มากมาย คำแนะนำของฉันคือให้คุณอ่านข้อกำหนดจนกว่าความเชื่อที่ผิดของคุณจะถูกแทนที่ด้วยความเชื่อที่แท้จริง

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

ที่มาของการแสดงออกไม่เกี่ยวข้องกับคำถาม กฎสำหรับการแสดงออกดังกล่าวมีการอธิบายไว้อย่างชัดเจนในข้อกำหนด; อ่านมัน!

มันควรจะเป็นการแทนที่หนึ่งบรรทัดสำหรับสำนวนเปรียบเทียบและแทนที่

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

อนึ่ง C # มีการเปรียบเทียบและแทนที่เป็นวิธีการห้องสมุดซึ่งสามารถ jitted ลงไปที่การเรียนการสอนเครื่อง ฉันเชื่อว่า Java ไม่มีวิธีการดังกล่าวเนื่องจากไม่สามารถแสดงในระบบประเภท Java


8
หากใครสามารถผ่าน JLS ได้ทั้งหมดก็ไม่มีเหตุผลที่จะตีพิมพ์หนังสือ Java และอย่างน้อยครึ่งหนึ่งของไซต์นี้ก็ไร้ประโยชน์เช่นกัน
John McClane

8
@ JohnMcClane: ฉันรับรองกับคุณว่าไม่มีปัญหาใด ๆ ในการทำสเปคทั้งหมด แต่คุณไม่จำเป็นต้องทำ ข้อมูลจำเพาะ Java เริ่มต้นด้วย "สารบัญ" ที่เป็นประโยชน์ซึ่งจะช่วยให้คุณเข้าถึงส่วนที่คุณสนใจได้อย่างรวดเร็วนอกจากนี้ยังออนไลน์และสามารถค้นหาคำหลักได้ ที่กล่าวว่าคุณถูกต้องมีทรัพยากรที่ดีมากมายที่จะช่วยให้คุณเรียนรู้วิธีการทำงานของ Java; คำแนะนำของฉันกับคุณคือคุณใช้ประโยชน์จากพวกเขา!
Eric Lippert

8
คำตอบนี้วางตัวและหยาบคายโดยไม่จำเป็น จำเอาไว้: ดีนะ
walen

7
@LuisG: ไม่มีการกำหนดเงื่อนไขโดยนัยหรือโดยนัย; เราทุกคนอยู่ที่นี่เพื่อเรียนรู้จากกันและกันและฉันไม่ได้แนะนำอะไรที่ฉันยังไม่ได้ทำเมื่อฉันเป็นมือใหม่ ไม่หยาบคาย เห็นได้ชัดว่าไม่น่าสงสัยและระบุความเชื่อผิด ๆ ของพวกเขาเป็นความเมตตาโปสเตอร์เดิม ซ่อนอยู่หลัง "สุภาพ" และช่วยให้คนที่จะยังคงมีความเชื่อผิด ๆ คือไม่ช่วยเหลือและตอกย้ำนิสัยที่ไม่ดีของความคิด
Eric Lippert

5
@ LuisG: ฉันเคยเขียนบล็อกเกี่ยวกับการออกแบบ JavaScript และความคิดเห็นที่เป็นประโยชน์ที่สุดที่ฉันเคยได้รับมาจากเบรนแดนชี้ให้เห็นอย่างชัดเจนและชัดเจนว่าฉันผิดที่ใด นั่นเป็นเรื่องที่ยอดเยี่ยมและฉันชื่นชมเขาที่สละเวลาเพราะฉันใช้ชีวิตในอีก 20 ปีข้างหน้าของฉันโดยไม่ได้ทำผิดพลาดซ้ำในงานของฉันเองหรือแย่กว่านั้นคือสอนให้คนอื่น มันยังให้โอกาสฉันในการแก้ไขความเชื่อผิด ๆ ที่เหมือนกันเหล่านั้นในผู้อื่นโดยใช้ตัวฉันเป็นตัวอย่างของคนที่เชื่อในสิ่งที่ผิด
Eric Lippert

16

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

วงเล็บ '()' มีลำดับความสำคัญสูงกว่าและมีการเชื่อมโยงจากซ้ายไปขวา Equality '==' มาถัดจากคำถามนี้และมีการเชื่อมโยงจากซ้ายไปขวา การมอบหมาย '=' มาครั้งสุดท้ายและมีการเชื่อมโยงจากขวาไปซ้าย

ระบบใช้สแต็กเพื่อประเมินการแสดงออก นิพจน์ได้รับการประเมินจากซ้ายไปขวา

ตอนนี้มาถึงคำถามเดิม:

int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false

x แรก (1) จะถูกผลักไปที่กอง จากนั้น Inner (x = y) จะถูกประเมินและผลักไปที่สแต็กด้วยค่า x (3) ตอนนี้ x (1) จะถูกเปรียบเทียบกับ x (3) ดังนั้นผลลัพธ์จึงเป็นเท็จ

x = 1; // reset
System.out.println((x = y) == x); // true

ที่นี่ (x = y) จะถูกประเมินตอนนี้ค่า x กลายเป็น 3 และ x (3) จะถูกผลักไปที่กองซ้อน ตอนนี้ x (3) ที่มีค่าการเปลี่ยนแปลงหลังจากความเท่าเทียมกันจะถูกผลักไปยังกองซ้อน ตอนนี้นิพจน์จะได้รับการประเมินและทั้งคู่จะเหมือนกันดังนั้นผลลัพธ์จึงเป็นจริง


12

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

ด้วย:

      x == (x = y)

โดยทั่วไปคุณกำลังทำสิ่งเดียวกันกับ:

      x == y

และxจะมีค่าเป็นyหลังจากการเปรียบเทียบ

ในขณะที่:

      (x = y) == x

โดยทั่วไปคุณกำลังทำสิ่งเดียวกันกับ:

      x == x

หลังจากxรับค่าของy และมันจะกลับมาจริงเสมอ


9

ในการทดสอบครั้งแรกที่คุณตรวจสอบจะมีค่า 1 == 3

ในการทดสอบครั้งที่สองการตรวจสอบของคุณจะทำ 3 == 3

(x = y) กำหนดค่าและทดสอบค่านั้น ในตัวอย่างก่อนหน้านี้ x = 1 ก่อนจากนั้นจึงกำหนด x 3 1 == 3 หรือไม่?

ในระยะหลัง x ได้รับมอบหมาย 3 และเห็นได้ชัดว่ามันยังคง 3 อยู่ 3 == 3 หรือไม่?


8

ลองพิจารณาตัวอย่างอื่นดูสิง่ายกว่านี้:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

ที่นี่ผู้ประกอบการที่เพิ่มขึ้นก่อน++xจะต้องนำไปใช้ก่อนที่จะทำการเปรียบเทียบ - เช่นเดียวกับ(x = y)ในตัวอย่างของคุณจะต้องคำนวณก่อนการเปรียบเทียบ

อย่างไรก็ตาม การประเมินผลการแสดงออกยังคงเกิดขึ้นได้ใส่→ไป→ขวาเพื่อเปรียบเทียบแรกเป็นจริงในขณะที่สองคือ1 == 2 สิ่งเดียวกันเกิดขึ้นในตัวอย่างของคุณ2 == 2


8

นิพจน์จะถูกประเมินจากซ้ายไปขวา ในกรณีนี้:

int x = 1;
int y = 3;

x == (x = y)) // false
x ==    t

- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
  x == t
  1 == 3 //false

(x = y) == x); // true
   t    == x

- left (x = y) => x = 3
           t    =      3 
-  (x = y) == x
-     t    == x
-     3    == 3 //true

5

โดยทั่วไปคำสั่งแรก x มีค่าเป็น 1 ดังนั้น Java จึงเปรียบเทียบ 1 == กับตัวแปร x ใหม่ซึ่งจะไม่เหมือนเดิม

ในอันที่สองคุณพูดว่า x = y ซึ่งหมายถึงค่าของ x เปลี่ยนแล้วดังนั้นเมื่อคุณเรียกมันอีกครั้งมันจะเป็นค่าเดียวกันดังนั้นทำไมมันถึงเป็นจริงและ x == x


4

== เป็นตัวดำเนินการเปรียบเทียบความเท่าเทียมกันและทำงานจากซ้ายไปขวา

x == (x = y);

นี่คือค่าเก่าที่กำหนดของ x ถูกเปรียบเทียบกับค่ากำหนดใหม่ของ x, (1 == 3) // false

(x = y) == x;

โดยที่นี่ค่าการมอบหมายใหม่ของ x ถูกเปรียบเทียบกับค่าการถือใหม่ของ x ที่กำหนดให้ก่อนการเปรียบเทียบ (3 == 3) // จริง

พิจารณาสิ่งนี้

    System.out.println((8 + (5 * 6)) * 9);
    System.out.println(8 + (5 * 6) * 9);
    System.out.println((8 + 5) * 6 * 9);
    System.out.println((8 + (5) * 6) * 9);
    System.out.println(8 + 5 * 6 * 9);

เอาท์พุท:

342

278

702

342

278

ดังนั้นวงเล็บมีบทบาทสำคัญในการแสดงออกทางคณิตศาสตร์ไม่เพียง แต่ในการเปรียบเทียบการแสดงออก


1
บทสรุปนั้นผิด พฤติกรรมไม่แตกต่างกันระหว่างตัวดำเนินการทางคณิตศาสตร์และตัวเปรียบเทียบ x + (x = y)และ(x = y) + xจะแสดงพฤติกรรมที่คล้ายกันกับต้นฉบับพร้อมตัวดำเนินการเปรียบเทียบ
JJJ

1
@JJJ ใน x + (x = y) และ (x = y) + x ไม่มีการเปรียบเทียบเกี่ยวข้องมันแค่กำหนดค่า y ให้กับ x และเพิ่มลงใน x
Nisrin Dhoondia

1
... ใช่นั่นคือประเด็น "วงเล็บมีบทบาทสำคัญในนิพจน์ทางคณิตศาสตร์เท่านั้นไม่ใช่ในนิพจน์เปรียบเทียบ"ผิดเนื่องจากไม่มีความแตกต่างระหว่างนิพจน์ทางคณิตศาสตร์และนิพจน์เปรียบเทียบ
JJJ

2

สิ่งที่นี่คือผู้ประกอบการเลขคณิต / สัมพันธ์เพื่อประกอบการ precedency ออกมาจากสองผู้ประกอบการ=เทียบ==ที่โดดเด่นเป็นหนึ่ง==(สัมพันธ์ผู้ประกอบการครอบงำ) ตามที่นำหน้า=ผู้ประกอบการที่ได้รับมอบหมาย แม้จะมีลำดับความสำคัญมาก่อนลำดับของการประเมินผลก็คือลำดับความสำคัญของ LTR (ซ้ายไปขวา) เข้ามาในรูปภาพหลังจากลำดับการประเมินผล ดังนั้นไม่ว่าการประเมินข้อ จำกัด ใด ๆ จะเป็น LTR


1
คำตอบนั้นผิด ลำดับความสำคัญของผู้ให้บริการจะไม่ส่งผลต่อลำดับการประเมิน อ่านบางส่วนของคำตอบที่ได้รับการโหวตบนสำหรับคำอธิบายโดยเฉพาะอย่างยิ่งคนนี้
JJJ

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

-1

มันง่ายในการเปรียบเทียบครั้งที่สองทางด้านซ้ายคือการมอบหมายหลังจากกำหนด y เป็น x (ทางซ้าย) แล้วคุณเปรียบเทียบ 3 == 3 ในตัวอย่างแรกคุณกำลังเปรียบเทียบ x = 1 กับมอบหมายใหม่ x = 3 ดูเหมือนว่า มีการอ่านสถานะการอ่านสถานะปัจจุบันเสมอจากซ้ายไปขวาของ x


-2

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

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


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