热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

为什么x==(x=y)与(x=y)==x不相同?

如何解决《为什么x==(x=y)与(x=y)==x不相同?》经验,为你挑选了10个好方法。

请考虑以下示例:

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语言规范中是否有一个项目要求加载变量的先前值以与右侧(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中应该使用更短的表达式.



1> Andrew Tobil..:

==是二元相等运算符.

评估右侧操作数的任何部分之前,似乎完全评估了二元运算符的左侧操作数.

Java 11规范>评估顺序>首先评估左侧操作数


"似乎是"意味着规范并不要求按时间顺序按顺序执行操作,但它要求您获得与之相同的结果.
措辞"似乎是"听起来并不像他们确定,tbh.
@MrLister"似乎"似乎是他们选择的一个糟糕的词."出现"意味着"向开发者表现为现象"."有效"可能是一个更好的短语.
在C++社区中,这相当于"as-if"规则......操作数需要表现为"好像"它是按照以下规则实现的,即使技术上并非如此.
@Kelvin我同意,我也会选择这个词,而不是"看起来像是".

2> 小智..:

正如LouisWasserman所说,表达式从左到右进行评估.并且java并不关心"评估"实际上做什么,它只关心生成(非易失性,最终)值来使用.

//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

需要注意的是第二个值将始终为true,无论初始值的xy,因为你有效地比较值将其分配给变量的赋值,a = bb会以该顺序进行评估,始终是相同的根据定义.



3> Lightness Ra..:

按括号隐含的顺序,应首先计算

不是.通常的误解是括号对计算或评估顺序有任何(一般)影响.它们只将表达式的部分强制转换为特定的树,将正确的操作数绑定到作业的正确操作.

(并且,如果你不使用它们,这些信息来自操作符的"优先级"和关联性,这是语言的语法树定义的结果.实际上,这仍然是它的工作原理使用括号,但我们简化并说我们当时不依赖于任何优先规则.)

一旦完成(即一旦你的代码被解析成一个程序),那些操作数仍然需要被评估,并且有关于如何完成的单独规则:所述规则(如安德鲁告诉我们的那样)说明每个操作的LHS首先在Java中进行评估.

请注意,并非所有语言都是如此; 例如,在C++中,除非您使用的是短路运营商像&&或者||,操作数的计算顺序一般是不明确的,你不应该依赖于它无论哪种方式.

教师需要使用诸如"这使得添加首先发生"之类的误导性短语来停止解释运算符优先级.给出一个表达式x * y + z,正确的解释是"运算符优先级使得加法发生在x * y和之间z,而不是在y和之间z",没有提到任何"顺序".


我希望我的老师在基础数学和他们用来表示它的语法之间做了一些分离,比如我们花了一天时间用罗马数字或波兰符号或其他什么,并看到添加具有相同的属性.我们在中学学习了相关性和所有这些属性,所以有充足的时间.
@JohnP:情况变得更糟.5*4是5 + 5 + 5 + 5还是4 + 4 + 4 + 4 + 4?有些老师坚持认为只有其中一种选择是正确的.
@Brian但是......但......实数的乘法是可交换的!
在我的思考世界中,一对括号表示"需要".计算'a*(b + c)',括号表示加法的结果_需要乘法.任何隐式操作员首选项都可以由parens,_except_ LHS-first或RHS-first规则表示.(这是真的吗?)@ Brian在数学中有一些罕见的情况,乘法可以通过重复加法来代替,但到目前为止并不总是正确的(从复数开始但不限于此).所以你的教育工作者应该真正关注那些告诉人们的事情......

4> Eric Lippert..:

我不确定Java语言规范中是否有一个项目要求加载变量的先前值...

有.下次您不清楚规范说明的内容时,请阅读规范,然后询问问题是否不清楚.

......按照(x = y)括号隐含的顺序,应首先计算右侧.

那句话是错误的.括号并不意味着评估顺序.在Java中,无论括号如何,评估顺序都是从左到右.括号确定子表达式边界的位置,而不是评估的顺序.

为什么第一个表达式求值为false,但第二个表达式求值为true?

==运算符的规则是:评估左侧生成值,评估右侧生成值,比较值,比较是表达式的值.

换句话说,意思expr1 == expr2总是temp1 = expr1; temp2 = expr2;和你写的然后评价一样temp1 == temp2.

=具有左侧局部变量的运算符的规则是:评估左侧以生成变量,评估右侧以生成值,执行赋值,结果是已分配的值.

所以把它放在一起:

x == (x = y)

我们有一个比较运算符.评估左侧以产生一个值 - 我们得到当前的值x.评估右侧:这是一个赋值,所以我们评估左侧产生变量 - 变量x- 我们评估右侧 - 当前值y- 分配给它x,结果是赋值.然后,我们将原始值与x分配的值进行比较.

你可以做(x = y) == x一个练习.再次,请记住,评估左侧的所有规则都发生在评估右侧的所有规则之前.

我希望首先评估(x = y),然后将x与自身(3)进行比较并返回true.

您的期望是基于对Java规则的一组错误信念.希望你现在有正确的信念,将来会期待真实的事情.

这个问题不同于"Java表达式中子表达式的评估顺序"

这句话是错误的.这个问题完全密切相关.

x绝对不是这里的"子表达式".

这句话也是错误的.在每个示例中,它是一个子表达式两次.

需要加载它以进行比较而不是"评估".

我不知道这是什么意思.

显然你仍然有很多错误的信念.我的建议是你阅读规范,直到你的错误信念被真正的信念所取代.

这个问题是特定于Java的,表达式x ==(x = y),与通常为棘手的面试问题精心设计的不可靠的不切实际的结构不同,来自一个真实的项目.

表达的出处与问题无关.规范中明确说明了这些表达的规则; 阅读!

它应该是比较和替换成语的单行替代品

由于这个单行替换引起了你的大量混淆,代码的读者,我认为这是一个糟糕的选择.使代码更简洁但更难理解并不是一个胜利.它不太可能使代码更快.

顺便提一下,C#已经比较和替换为库方法,可以将其下载到机器指令.我相信Java没有这样的方法,因为它无法在Java类型系统中表示.


如果有人可以通过整个JLS,那么没有理由发布Java书籍,至少有一半的这个网站也没用.
@JohnMcClane:我向你保证,在完成整个规范时没有任何困难,但你也没有.Java规范从一个有用的"目录"开始,它将帮助您快速访问您最感兴趣的部分.它还可以在线和关键字搜索.那就是说,你是对的:有很多好的资源可以帮助你了解Java的工作原理; 我给你的建议是你利用它们!
这个答案是不必要的居高临下和粗鲁.记住:[很高兴](/sf/ask/17360801/).
@LuisG.:无意或暗示的屈尊; 我们都在这里互相学习,我不推荐任何我初学时没有做过的事情.也不粗鲁.**明确无误地确定他们的错误信念是对原始海报**的善意.躲在"礼貌"背后,让人们继续拥有错误的信念是*无益的*,*强化了不良的思想习惯*.
@LuisG.:我曾经写过一篇关于Javascript设计的博客,我收到的最有帮助的评论来自Brendan,清楚而明确地指出我弄错了.这很棒,我很感激他花时间,因为我生活在接下来的20年里,不是在我自己的工作中重复这个错误,或者更糟糕的是,教给别人.它还让我有机会通过将自己作为人们如何相信虚假事物的一个例子来纠正其他人的错误信念.
EricLippert我非常尊重你,但我确实认为在强烈和突然之间有一些中间立场与一个人的建议(并且很遗憾地说,我认为这就是这种情况)和同情.IMO,你的建议有一个粗线程的感觉,当细线也可以工作.¯\\ _(ツ)_ /¯而且这是真的,我的原始建议不会为你立即寻求的启蒙提供工具,但我相信它会带来更少的摩擦和更大的合作.

5> Amit..:

它与运算符优先级以及如何评估运算符有关.

括号'()'具有更高的优先级,并且从左到右具有关联性.平等'=='在这个问题中接下来,并且从左到右具有相关性.作业'='是最后一个,从右到左有结合性.

系统使用堆栈来评估表达式.表达式从左到右进行评估.

现在来到原始问题:

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

首先将x(1)推到堆叠状态.然后将评估内部(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)在相等之后具有更改的值将被推送到堆栈.现在将对表达式进行求值,两者都是相同的,因此结果为真.



6> Or10n..:

它不一样.左侧将始终在右侧之前进行评估,并且括号不指定执行顺序,而是指定一组命令.

附:

      x == (x = y)

你基本上是这样做的:

      x == y

并且在比较之后x将具有y的值.

同时:

      (x = y) == x

你基本上是这样做的:

      x == x

xy的价值之后.而且它将永远回归真实.



7> Michael Puck..:

在第一次测试中,你检查1 == 3.

在第二次测试中,您的检查确实为3 == 3.

(x = y)分配值并测试该值.在前一个例子中,x = 1,然后x被赋值为3. 1 == 3?

在后者中,x被赋值为3,显然它仍然是3. 3 == 3?



8> walen..:

考虑另一个,也许更简单的例子:

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.
你的例子中也会发生同样的事情.



9> Derviş Kayım..:

表达式从左到右进行评估.在这种情况下:

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



10> 小智..:

基本上第一个语句x有它的值1所以Java将1 ==与新的x变量进行比较,这个变量将不相同

在第二个你说x = y这意味着x的值发生了变化,所以当你再次调用它时它将是相同的值,因此为什么它是真的而x == x


推荐阅读
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
author-avatar
MYJIE2502897603
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有