2

ブール値の大規模なデータベースがあり、すべての値に対してクエリを簡単に実行するためのフレームワークを構築したいと考えています。これを行うには、ブール式の文字列表現を指定して、データベースのすべての要素に対してその式を評価する関数を作成します。たとえば、与えられた入力

(a && b) || c

関数は、評価する別の関数を作成します

return (funcA() && funcB()) || funcC();

ここfuncAで、、、funcBおよびfuncCはブール値を返す関数です

4

5 に答える 5

5

これは、3つのステップで行うのが最適なようです。

まず、何を評価するのかを正確に把握する必要があります。これは通常、スキャン解析と呼ばれる2つのステップで実行されます。スキャンの役割は、入力文字列を一連のトークン、つまりテキストを構成する小さな論理単位に分割することです。たとえば、文字列が与えられた

(a && b)

これをトークンに分割します

(
a
&&
b
)

通常、これは正規表現を使用して行われますが、手動で行うこともできます。主なアイデアは、文字列の断片を決定するタスクを、それらの断片がどのように関連しているかを確認するタスクから分離することです。

入力をスキャンしたら、それを解析して何が言われているかを判断する必要があります。つまり、トークンを完全な数式エンコーディング演算子の優先順位、使用されているオペランドなどに再構成します。これを行うためのアルゴリズムは多数ありますが、おそらく最も簡単なのは、ダイクストラの操車場アルゴリズムです。埋め込む。この解析ステップの出力は、入力の構造をエンコードするツリー構造である抽象構文ツリーを使用して保存する可能性があります。

この時点で、評価する式の意味を明確に解釈できるので、実際に評価する必要があります。これを行うには、おそらく、ASTノードごとに、そのノードから値を生成する関数を定義します。&&のような演算子の場合、左と右の部分式を評価してから、それらのANDを計算します(または、lhsがfalseの場合は、短絡を使用してrhsの計算を回避します)。個々の文字については、リフレクションを使用して対応するメソッドを呼び出すか、(必要なセキュリティに応じて)名前を関数にマッピングするテーブルを作成できます。

コーディングに関する潜在的な最適化として、ASTの構築を省略し、必要な値を計算することを検討することをお勧めします。操車場アルゴリズム(およびトップダウンLL(1)やボトムアップLR(1)パーサーなど、他の多くのパーサー)では、通常、構成式の観点から式の全体的な値を計算できます。この方法でコーディングする方が簡単な場合があります。ただし、データベースなどの巨大なデータセットで説明されている関数を使用することを計画している場合、ASTを計算すると、データベース内の各値で呼び出して必要な値を生成できるオブジェクトが得られます。

膨大なデータセットに対して非常に複雑なクエリを実行することを計画している場合は、さらに一歩進んで、生成された式を実際にC#コードに変換し、それをコンパイルして実行中のプログラムにロードすることもできます。これが非常に効果的に使用されたJavaの例を見てきましたが、これは非常に高性能なアプリケーション用であり、他のすべてのオプションを使い果たしていない限り、おそらくやり過ぎです。

お役に立てれば!

于 2011-08-31T08:18:53.390 に答える
2

これが私の選択したソリューションです。

私は次のコードプロジェクトを使用しています

http://www.codeproject.com/KB/dotnet/Expr.aspx

たとえば、サインとルールIDのリストを取得します。ArgsList = List<string> ={"0","&&","5"} // (0&&5)

   int id;
   var tmp = new List<string>();
   //------------------------------//
   foreach( string arg in ArgsList)
   {
       if( ( arg != "&&" && arg != "||" && arg != ")" && arg != "(" ) )
       {
          try
          {
              id = int.Parse(arg);
          }
          catch( Exception ex )
          {
               return false;
          }
          tmp.Add(GetRuleById(id, ref errorString).Check(wwObject, ref errorString).ToString());
       }
       else
       {
            tmp.Add(arg);
       }
  }

  //foreach converts it to List<string> = {"True","&&","False"}
  string stringtoeval;
  stringtoeval = string.Join(string.Empty, tmp.ToArray()).ToLower();//"True&&False"
  return (bool)EvalCSCode.EvalCSCode.Eval(stringtoeval);//returns false
于 2011-09-01T07:09:11.133 に答える
1

かっこがあるので、最初に評価する必要のある部分式について、かっこを(再帰的に、スタック上で)解析する必要があります。演算子(&&、||、!)と記号(a、b、c)を解析し、それらを適切な論理演算子または関数呼び出しに置き換える必要があります。

あなたを始めるために:

!で始まらない限り、記号で始まります。オペレーター。

記号で始める場合、次の文字は二項演算子(&&、||)にする方が適切です。そして、その後の文字は部分表現または記号である方がよいでしょう。部分式の場合は、再帰的に評価します。記号の場合は、中央にある演算子をオフにし、必要に応じてANDまたはORを組み合わせて、値を返します。

于 2011-08-31T07:43:23.183 に答える
1

これは、入力文字列を解析し、リフレクションを使用して実行するメソッドを作成し、それらを実行することで実現できますが、これはかなり複雑なソリューションです。これで何を達成しようとしていますか?ラムダと式ツリーおよびデリゲートを使用してそれを行うためのより良い方法があるかもしれません。

于 2011-08-31T07:47:02.317 に答える
0

解析の詳細に入る代わりに、これは.NETリフレクションを使用して実行できると思います(C#タグが表示されているので、このソリューションで問題がないことを願っています)。リフレクションを使用すると、特定の式を評価するメソッドが発行され、このメソッドを呼び出して結果が得られます。個人的には、このためのパーサーを作成することは、.NETリフレクションを使用するよりも困難で時間がかかると感じています。

于 2011-08-31T08:37:27.517 に答える