あいまいな文法が悪いのはなぜですか?


30

左右に2つ以上の派生ツリーが存在する場合、文法があいまいであることを理解しますが、なぜそれがあまりにもひどくて誰もがそれを取り除きたいと思うのか理解できません。


1
関連するが同一ではない:softwareengineering.stackexchange.com/q/343872/206652(免責事項:受け入れられた答えを書きました)
marstato

明確な文法を見つける」も参照してください。
ロブ

1
実際、曖昧さのない形式は実際の使用に適しています。曖昧さのない形式はプロダクションルールの数が少ないため、小さなツリーを高いレベルで構築します(したがって、効率的なコンパイラが解析にかかる時間を短縮します)。ほとんどのツールは、曖昧さをサイドグラマーから明示的に解決する機能を提供します。
グリジェシュチャウハン

3
「誰もがそれを取り除きたい」。まあ、それは本当ではありません。商業的に関連する言語では、言語が進化するにつれて曖昧さが追加されるのが一般的です。たとえば、C ++ std::vector<std::vector<int>>は2011年に意図的にあいまいさを追加しました>>。重要な洞察は、これらの言語はベンダーよりもはるかに多くのユーザーを持っているため、ユーザーにとってささいな煩わしさを修正することは、実装者による多くの作業を正当化することです。
MSalters

回答:


52

算術式のため、以下の文法を考えてみましょう:

XX+XXXXXX/Xvarconst
:次の式を検討 - B - C 、その値がどのようなものですか?次に、2つの可能な解析ツリーを示します。
abc

(X-X)-X ここに画像の説明を入力してください

左側のものによればabc(ab)cとして解釈する必要があります。これは通常の解釈です。右側の1つによると、これをa(bc)=ab+cとして解釈する必要がありますが、これはおそらく意図したものではありません。

プログラムをコンパイルするとき、構文の解釈を明確にする必要があります。これを強制する最も簡単な方法は、明確な文法を使用することです。文法があいまいな場合は、演算子の優先順位や結合性などのタイブレークルールを提供できます。これらの規則は、特定の方法で文法を明確にすることで同等に表現できます。


構文木ジェネレーターを使用して生成された木を解析します


12
@HIRAKMONDAL構文があいまいであるという事実は、実際の問題ではありません。問題は、2つの異なる解析ツリーの動作が異なることです。あなたの言語が曖昧な文法を持っているが、式のすべての構文解析木が意味的に同等であるなら、それは問題ではないでしょう(例えば、Yuvalの例を取り、あなたの唯一の演算子の場合を考えてください+)。
バクリウ

14
@Bakuriuあなたが言ったことは本当ですが、「意味的に同等」というのは高い順序です。たとえば、浮動小数点演算は実際には結合的ではありません(したがって、2つの「+」ツリーは等価ではありません)。さらに、答えが同じようになったとしても、式に副作用が生じる可能性のある言語では、未定義の評価順序が重要になります。あなたの言ったことは技術的には真実ですが、実際には、文法の曖昧さがその文法の使用に影響を与えないことは非常に珍しいことです。
リチャードラスト

現在、一部の言語では加算で整数のオーバーフローがチェックされるため、整数のa + b + cでさえ評価の順序に依存します。
gnasher729

3
さらに悪いことに、場合によっては、文法は代替の意味を達成する方法を提供しません。クエリ言語でこれを見てきました。エスケープ文法(エスケープする特殊文字を2倍にするなど)を選択すると、特定のクエリを表現できなくなります。
ハーミングモニカを

12

他の既存の回答【とは対照的に12 ]、確かに曖昧な文法である適用分野、ある有用。自然言語処理(NLP)の分野で、自然言語(NL)を正式な文法で解析したい場合、NLはさまざまなレベルで本質的に曖昧であるという問題を抱えています[Koh18、ch。6.4]:

  • 構文の曖昧さ:

    ピーターは赤いスポーツカーで男を追いかけた

    赤いスポーツカーに乗っていたのはピーターですか?

  • 意味的な曖昧さ:

    ピーターは銀行に行きました

    座る銀行またはお金を引き出す銀行ですか?

  • 実用的な曖昧さ:

    二人の男が二つの袋を運んだ

    彼らは一緒にバッグを運んだのですか、それとも各人が2つのバッグを運んだのですか

NLPのさまざまなアプローチは、一般に処理、特にこれらの曖昧さを処理します。たとえば、パイプラインは次のようになります。

  1. 曖昧な文法でNLを解析する
  2. 生成されるすべてのASTに対して、モデル生成を実行して、曖昧なセマンティックな意味を生成し、ステップ1から不可能な構文のあいまいさを排除します
  3. 結果のモデルごとに、キャッシュに保存します。

すべての文に対してこのパイプラインを実行します。たとえば、処理する同じ本のテキストが多いほど、前の文からステップ3まで存続した不可能な余分なモデルを除外できます。

プログラミング言語とは対照的に、すべてのNL文が正確なセマンティクスを持つという要件を手放すことができます。代わりに、大きなテキストの解析全体で複数の可能なセマンティックモデルをブックキープできます。しばらくの間、後の洞察は以前のあいまいさを排除するのに役立ちます。

曖昧な文法の複数の派生を出力できるパーサーで手を汚したい場合は、Grammatical Frameworkを見てください。また、[Koh18、ch。5]は、上記の私のパイプラインに似たものを示す紹介です。ただし、[Koh18]は講義ノートであるため、講義なしではノート自体が簡単に理解できない場合があることに注意してください。


参照資料

[Koh18]:マイケルコールハース。「論理ベースの自然言語処理。冬学期2018/19。講義ノート。」URL:https://kwarc.info/teaching/LBS/notes.pdf。コースの説明のURL:https : //kwarc.info/courses/lbs/(ドイツ語)

[Koh18、ch。5]:[Koh18]の第5章「フラグメントの実装:文法的および論理的フレームワーク」を参照してください。

[Koh18、ch。6.4] [Koh18]の6.4章「あいまいさの計算上の役割」を参照


おかげでトン..私は同じ疑問を持っていたし、uはそれをクリア... :)
HIRAK MONDAL

1
問題は言うまでもありません バッファローバッファローバッファローバッファロー水牛水牛...水牛の適切な数のために
ハーゲン・フォン・Eitzen

あなたは「対照的に」と書いていますが、私はこれを私が答えたもののコインの反対側と呼びます。曖昧な文法を使用した自然言語の解析は非常に難しいため、従来のパーサーでは実行できません。
デイビスラー

1
@ComFreekここでもっと正確にすべきです。GF(リンクをお寄せいただきありがとうございます!)を簡単に見ると、3つの拡張機能(重複の許可など)を使用してコンテキストに依存しない文法を読み取り、可能なすべての派生物のリストを返すことがわかります。そのためのアルゴリズムは、1950年代から存在していました。ただし、完全に一般的なCFGを処理できるということは、最悪の場合のランタイムが爆発することを意味し、実際には、GLLなどの一般的なパーサーを使用する場合でも、ソフトウェアエンジニアはLL文法などのCFGのサブセットを使用しようとしますより効率的に解析されます。
デイビスラー

1
@ComFreekだから、コンピューターがCFGを処理できないわけではありません(ただし、自然言語は実際にはコンテキストに依存しておらず、実際に役立つ機械翻訳はまったく異なる手法を使用しています)。あいまいさを処理するためにパーサーが必要な場合、それはより効率的になる特定のショートカットを除外するということです。
デイビスラー

10

あいまいさを処理する明確な方法があったとしても(あいまいな式は構文エラーなど)、これらの文法は依然として問題を引き起こします。文法にあいまいさを導入するとすぐに、パーサーは、最初に一致したものが決定的であるかどうかを確認できなくなります。あいまいさを排除するために、ステートメントを解析する他のすべての方法を試行し続ける必要があります。また、LL(1)言語のような単純なものを扱っていないため、単純で小さく高速なパーサーを使用できません。文法には複数の方法で読み取れる記号が含まれているため、多くのバックトラックを準備する必要があります。

一部の制限されたドメインでは、式の解析に考えられるすべての方法が同等であることを証明できる場合があります(たとえば、連想操作を表すため)。(a + b)+ c = a +(b + c)。


9

DOES IF a THEN IF b THEN x ELSE y平均

IF a THEN
    IF b THEN
        x
    ELSE
        y

または

IF a THEN
    IF b THEN x
ELSE
    y

?別名宙ぶらりんの問題


1
これは、あいまいでない文法(Java、C、C ++など)でさえ、人間の観点から見た目(!)のあいまいさを許可することを示す良い例です。正式かつ計算上は問題ありませんが、UX /バグのない開発の問題が増えました。
ComFreek

5

たとえば、C ++で最も厄介な解析を行います。

bar foo(foobar());

これはfoo、型の関数宣言bar(foobar())(パラメーターはを返す関数ポインターですfoobar)か、foo型の変数宣言でint、デフォルトの初期化で初期化されていますfoobarか?

これは、パラメーターリスト内の式を型として解釈できない場合を除き、最初のパラメーターを想定することでコンパイラーで区別されます。

このようなあいまいな式を取得すると、コンパイラには2つのオプションがあります

  1. 式が特定の派生であると想定し、他の派生を表現できるように文法に曖昧さを取り除くものを追加します。

  2. いずれかの方法でエラーが発生し、曖昧性解消が必要

最初のものは自然に抜け落ち、2番目のものはコンパイラプログラマが曖昧さを知っていることを要求します。

このあいまいさが検出されないままの場合、2つの異なるコンパイラーがそのあいまいな式の異なる派生をデフォルトとする可能性があります。明白ではない理由により、コードが移植不能になる。これは、実際には言語仕様の欠陥である一方、コンパイラの1つのバグであると人々に思わせます。


5

この質問には、せいぜい正しい境界線のみであるという仮定が含まれていると思います。

実際の生活では、あいまいな文法(あいにく)があいまいでない限り、単にあいまいな文法と一緒に暮らすことはかなり一般的です。

たとえば、yacc(またはbisonやbyaccなどの類似の)でコンパイルされた文法を見てみると、コンパイル時に「N shift / reduct conflicts」に関する警告がかなりの数生成されることがわかります。yaccがshift / reduceコンフリクトに遭遇すると、それは文法のあいまいさを示します。

ただし、シフト/リデュースの競合は、通常、かなり小さな問題です。パーサージェネレーターは、reduceではなく「shift」を優先して競合を解決します。それがあなたが望むものであるなら、文法は完全に素晴らしいです(そして、それは実際に完全にうまくいくようです)。

通常、この一般的な順序のケースでは、シフト/リデュースの競合が発生します(非ターミナルにはキャップを使用し、ターミナルには小文字を使用します)。

A -> B | c
B -> a | c

に遭遇するcと、あいまいさがあります:をc直接解析するA必要BがありAますか、それとも解析する必要がありますか?このような場合、yaccなどはより単純な/より短いルートを選択し、-> -> ルートではなく、cとして直接解析します。これは間違っている可能性がありますが、もしそうなら、おそらく文法に非常に単純なエラーがあることを意味し、可能性としてオプションをまったく許可すべきではありません。AcBAcA

これで、対照的に、次のようになります。

A -> B | C
B -> a | c
C -> b | c

私たちが遭遇したときに今、c私たちは、治療するかどうかの間に矛盾していcBC。自動競合解決戦略が本当に必要なものを選択する可能性はずっと低くなります。これらはどちらも「シフト」ではなく、どちらも「削減」であるため、これは「競合の削減/削減」です(yaccなどに慣れている人は、一般に、シフト/削減の競合よりもはるかに大きな問題として認識します)。

だから、私は誰もが文法のあいまいさを本当に歓迎していると言っているかどうかはわかりませんが、少なくともいくつかのケースでは、誰もそれについてあまり気にしていないほど十分にマイナーです。要約では、彼らはすべての曖昧さを除去するというアイデアを好むかもしれませんが、常に実際にそれを行うのに十分ではありません。たとえば、わずかなあいまいさを含む小さく単純な文法は、あいまいさを排除するより大きく複雑な文法よりも好ましい場合があります(特に、実際に文法からパーサーを生成し、曖昧でないことを見つける実用的な領域に入る場合)文法は、ターゲットマシンで実行されないパーサーを生成します)。


5か月前にシフトとリデュースの競合についてこの素晴らしい説明があったらいいのにと思います!^^; +1
ホテルカリフォルニア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.