プログラミング言語理論では、演算子の結合性とは、括弧がない場合に同じ優先順位の演算子がどのようにグループ化されるかを決定する特性です。オペランドの前後に演算子 (たとえば、) があり、それらの演算子の優先順位が同じである場合、オペランドは 2 つの異なる操作 (つまり、2 つの演算子によって示される 2 つの操作) への入力として使用できます。オペランドをどの操作に適用するかは、演算子の結合性によって決定されます。演算子は、結合性(操作を任意にグループ化できる)、左結合性(操作が左からグループ化される)、右結合性(操作が右からグループ化される)、または非結合性(出力型が入力型と互換性がないことが多いため、操作を連鎖できない) のいずれかになります。演算子の結合性と優先順位は、プログラミング言語の定義の一部です。プログラミング言語によって、同じ種類の演算子でも結合規則や優先順位が異なる場合があります。^ 3 ^
式 を考えてみましょうa ~ b ~ c。演算子が~左結合性を持つ場合、この式は と解釈されます(a ~ b) ~ c。演算子が右結合性を持つ場合、この式は と解釈されますa ~ (b ~ c)。演算子が非結合性の場合、この式は構文エラーであるか、特別な意味を持つ可能性があります。数学演算子の中には、固有の結合性を持つものがあります。たとえば、従来の数学表記で使用される減算と除算は、本質的に左結合です。対照的に、加算と乗算は、左結合と右結合の両方です。(例: (a * b) * c = a * (b * c))。
多くのプログラミング言語のマニュアルには、演算子の優先順位と結合規則の表が掲載されています。例えば、C と C++ の表を参照してください。
ここで説明する記号的結合法則の概念は、数学的結合法則と関連していますが、異なります。数学的に結合法則を満たす演算は、定義上、記号的結合法則を必要としません。(例えば、加算は結合法則を満たすため、左結合法則でも右結合法則でも必要ありません。)一方、数学的に結合法則を満たさない演算は、記号的に左結合法則、右結合法則、または非結合法則を満たす必要があります。(例えば、減算は結合法則を満たさないため、記号的結合法則を満たす必要があります。)
結合法則は、式内の演算子の優先順位が同じ場合にのみ必要です。通常+、とは-同じ優先順位を持ちます。式を考えてみ7 - 4 + 2ましょう。結果は、(7 - 4) + 2 = 5またはのいずれかになります7 - (4 + 2) = 1。前者の結果は、+とが-左結合の場合に対応し、後者は+、と-が右結合の場合に対応します。
通常の使用法を反映するため、加算、減算、乗算、除算演算子は通常左結合ですが、[ 1 ] [ 2 ] [ 3 ]べき乗演算子(存在する場合) [ 4 ]については一般的な合意はありません。代入演算子は通常右結合です。オペランドが 2 つの演算子に関連付けられたり、演算子がまったく関連付けられなかったりするケースを防ぐため、同じ優先順位の演算子は同じ結合性を持つ必要があります。
式 を考えてみましょう5^4^3^2。ここで は^右結合のべき乗演算子とみなされます。左から右にトークンを読み込むパーサーは、 の右結合性のため^、次のようにブランチに結合規則を適用します。
5が読み上げられます。^記号が読み取られます。ノード: " 5^"。4が読み込まれました。ノード: " 5^4"。^記号が読み取られ、右結合規則がトリガーされます。結合規則によりノードが決定されます: " 5^(4^"。3が読み込まれました。ノード: " 5^(4^3"。^記号が読み取られ、右結合規則の再適用がトリガーされます。ノード " 5^(4^(3^"。2が読み込まれました。ノード " 5^(4^(3^2"。5^(4^(3^2))」を生成します。これは、最上位ノード(最初のノード^)から始めて、深さ優先で評価できます。
^。^。^。左結合評価では構文木 ((5^4)^3)^2とまったく異なる結果 (625 3 ) 2 = 244,140,625 2 ≈が得られただろう5.960 4645 × 10 16。
多くの命令型プログラミング言語では、代入演算子は右結合性を持つように定義されており、代入は単なる文ではなく、式(値に評価される式)として定義されています。これにより、ある代入式の値を次の代入式の右オペランドとして使用することで、連鎖的な代入が可能になります。
Cでは、代入は、の型に変換されたa = b式と同じ値に評価される式であり、副作用として のR 値をのL 値に格納します。[ a ]したがって、式は と解釈できます。代替式は、 が L 値式ではないため、つまり R 値はありますが、 の R 値を格納する L 値がないため、エラーになります。演算子の右結合性により、 のような式はと解釈できます。babaa = (b = c)b = c; a = b;(a = b) = ca = bc=a = b = ca = (b = c)
C++では、代入はa = b式と同じ値に評価される式でありa、副作用として の右辺の値が の左辺の値に格納されますb。aしたがって、式a = (b = c)は と解釈できますb = c; a = b;。また、代替式はエラーを発生させる代わり(a = b) = cに と解釈できますa = b; a = c;。演算子の右結合性により、=のような式はa = b = cと解釈できますa = (b = c)。
非結合演算子とは、式の中で連続して使用された場合の動作が定義されていない演算子のことです。Prologでは、中置演算子は非結合:-演算子です。なぜなら、「 」のような構造は構文エラーとなるからです。a :- b :- c
別の可能性としては、特定の演算子のシーケンスが、結合法則として表現できない別の方法で解釈されるということが考えられます。これは一般的に、構文的にはこれらの操作のシーケンスに特別な規則があり、意味的には動作が異なることを意味します。良い例は、このような構造がいくつかあるPythonです。[ 5 ]代入はステートメントであり、操作ではないため、代入演算子には値がなく、結合法則も成り立ちません。代わりに、代入のシーケンスに対する文法規則を用意し、左から右に代入することで、連鎖代入が実装されます。さらに、代入と拡張代入の組み合わせ、例えばは、C では合法ですが、Python では合法ではありません。別の例として、 、、 などの比較演算子があります。 のような連鎖比較は と解釈され、 またはとは等価ではありません。[ 6 ]a = b = ca = b += c>==<=a < b < c(a < b) and (b < c)(a < b) < ca < (b < c)