我对从左到右和从右到左的相关性的定义感到困惑。我也看到他们被称为左关联性和右关联性,并想知道哪个对应于哪个。
我知道它与执行具有相同优先级的操作的顺序有关,如a = x * y * z表示a = x *(y * z)或a =(x * y)* z 。我不知道哪一个是从左到右的关联,哪个是从右到左的关联。
我尝试过谷歌它,但我能找到的只是表格中不同运算符在c ++中的关联性。看看所有的例子让我更加困惑。
进一步让我感到困惑的是:
glm::vec4 transformedVector = translationMatrix * rotationMatrix * scaleMatrix * originalVector;
首先执行缩放矩阵乘法,然后是旋转矩阵,然后是平移。在这个例子中,矩阵都是glm :: mat4类型,向量是glm :: vec4类型。这是从左到右还是从右到左的关联性?这与正常乘法相同还是glm类型的乘法不同?
答案 0 :(得分:22)
您通常从左到右阅读。你通常从左到右做数学。这是左右相关性,最常见。
大多数人都会解决
x = 23 + 34 + 45
通过分组
x = (23 + 34) + 45
这是从左到右的关联性。你可以记住它,因为你从左到右阅读并做数学。
对于数学中的 ,它并不重要。你总是得到相同的结果。这是因为添加关联。说一个操作是关联意味着从左到右和从右到左的关联是相同的事情。对于编程,它仍然很重要,因为溢出和浮点运算(但不适用于任何合理语言的正常大小的整数),所以当你有一个大数字和轻浮的2 AM错误使用a+b
和b+a
时,请记住添加的顺序。
在你的例子中:
glm::vec4 transformedVector = translationMatrix * rotationMatrix * scaleMatrix * originalVector
你从概念上首先从右侧开始,因为那是你所采取行动的地方。但是在C ++中,*
通常是从左到右的关联,并且无法覆盖此。 glm可以通过多种方式处理这个问题:它可能会构建一个等待最终向量到达的事物的缓存然后从右到左进行乘法。它也可能(更有可能)使用矩阵乘法完全关联的代数定理,然后从左到右乘以,然后向文档中的读者保证它与从右到左的思维方式相同。但是,您需要了解实施,因为如前所述[{3}}。
为完整起见,请考虑减法。什么是a - b - c
?这里确实无论是左对联还是右对联都很重要。当然,在数学中我们将它定义为b (a - b) - c
,但是一些奇怪的编程语言可能更喜欢减法为右关联,并且a - b - c
总是意味着a - (b - c)
。这种外来语最好有一个文档页面,指明-
是右关联的,因为它是操作规范的一部分,而不是你只能通过查看操作符的使用来判断。
答案 1 :(得分:6)
您可以从以下字词中看到:
当我们将运算符组合成表达式时,其顺序在哪里 运营商的应用可能并不明显。例如, a + b + c可以解释为((a + b)+ c)或(a +(b + c))。 我们说如果操作数被分组为左,则+是左关联的 正如((a + b)+ c)所见。我们说它是正确联想的 将操作数分组在相反方向,如(a +(b + c))。
A.V。 Aho& J.D. Ullman 1977,p。 47
答案 2 :(得分:3)
最简单,最不安全;博士回答我发现:
在大多数编程语言中,加法,减法,乘法和除法运算符是左关联,而赋值,条件和取幂运算符是右关联。
答案 3 :(得分:2)
a = (x * y) * z
是从左到右,a = x * (y * z)
是从右到左。
glm的矩阵乘法从左到右关联,因为它会使*
运算符重载。这里的问题是关于几何变换的矩阵乘法的意义,而不是数学相关性。
答案 4 :(得分:2)
如果在没有明确括号的情况下嵌套使用此运算符(表达式类型),则中缀运算符(或更一般地,具有未闭合的左和右子表达式的表达式类型)是左关联的,隐式括号放置在左侧。由于*
在C ++中是左关联的,因此a*b*c
表示(a*b)*c
。如果嵌套更深,则左端会出现一组隐式括号:(((a*b)*c)*d)*e
。
等效地,这意味着此运算符的语法生成规则是左递归的(意味着左子表达式具有与此规则生成的语法类别相同的语法类别,因此相同的规则(相同的运算符)可能是直接用于形成该子表达式;另一端的子表达式具有更严格的句法类别,并且使用相同的运算符将需要显式括号)。在C ++中,乘法表达式的一个产生(标准中的5.6节)读取 mutliplicative-expression *
pm-expression ,在左侧使用乘法表达式。
因此,在没有明确括号的嵌套用法中,最左边的运算符将其直接邻居作为操作数,而其他实例将左边的操作数作为左边所有内容形成的表达式(结果)。
我承认,我已经推动了这一点(太远了)。我的观点在于,#34;右边"发生,也没有任何动静;结合性是一种句法因而是静态的。重要的是 隐含的括号,而不是以哪种顺序写入它们(实际上一个根本没有,或者它们是显式的)。当然,对于右关联性,只需更换每个" left"通过"对"上方。
总之,我看不出为什么人们应该将这种从左到右的关联性(或分组)称为无效的理由,但事实是人们做的事情(即使标准确实如此,尽管鉴于明确的语法,它完全是多余的规则也给出了。)
混淆来自解释这一点,正如通常所做的那样,通过说(在没有明确的括号的情况下)操作符从左到右执行(对于右关联运算符,分别从右到左)。这是误导性的,因为它将语法与语义(执行)混淆,并且仅对具有自下而上评估的操作有效(所有操作数在运算符之前进行评估)。对于有特殊评估规则的操作员来说,这是完全错误对于运算符&&
(和)和||
(或),语义首先计算左操作数,然后运算符本身(即决定左操作数或右操作数是否会产生结果)跟随< em>可能通过评估右操作数。这种从左到右的评估完全独立于关联性:运算符碰巧是左关联的,可能是因为所有非赋值的二元运算符都是(c1 && c2) && c3
(带有冗余括号的地方,它们已经隐含了)具有与c1 && (c2 && c3)
等效的执行(即从左到右执行条件,直到一个返回false
并返回,或者如果没有返回true
),我无法想象一个合理的编译器为这两种情况生成不同的代码。实际上我发现正确的分组更能说明表达式的评估方式,但它确实没有区别;同样适用于or
。
对于条件(三元)运算符? ... :
,这一点更为明确。这里结合性适用,因为两边都有开放的子表达(参见我的开头句);中间操作数括在?
和:
中,而从不需要额外的括号。实际上,此运算符被声明为正确 - 关联,这意味着c1 ? x : c2 ? y : z
应该被读作c1 ? x : (c2 ? y : z)
而不是(c1 ? x : c2) ? y : z
(隐含的括号是对)。但是,使用隐式括号,两个三元运算符从左到右执行 ;解释是三元运算符的语义不首先评估所有子表达式。
回到你问题的例子,左关联性(或从左到右分组)意味着你的矩阵向量产品被解析为((M1*M2)*M3)*v
。虽然在数学上是等价的,但实际上不可能将其作为M1*(M2*(M3*v))
执行,即使这样更有效。原因是浮点乘法不是真正的关联(只是近似),因此也不是浮点矩阵乘法;因此,编译器无法将一个表达式转换为另一个表达式。请注意,在((M1*M2)*M3)*v
中,不能说哪个矩阵首先应用于向量,因为它们都不是:复合线性映射的矩阵首先被计算,而 矩阵应用于向量。结果大约等于M1*(M2*(M3*v))
应用M3
的结果,然后是M2
,最后是M1
。但如果你想要发生这样的事情,你必须写下这些括号。
答案 5 :(得分:-1)