PHP Manual
/
数学

PHP の電卓: 数式を文字列として処理する

16. 02. 2020

Obsah článku

ユーザーが検索ボックスにテキスト文字列として入力した簡単な数学の例題を処理するタスクに直面したとします。通常、ユーザーは数字を使った簡単な数値演算を行いたい。この記事では、その思考回路と具体的な方法を解説しています。

ナイーブ実装

長い間、単純な数式を何らかのトリックで処理して、コードをできるだけ短くできないかと考えていたのですが、何年か経って、実際にその解決策を手に入れました。

なぜなら、これは極めて危険なものであり、不正なユーザが簡単に文字列の下線を引いて、例えば、アプリケーション全体を削除したり、データベースを盗んだりすることができるからです!

// ユーザークエリ
$query = '5 + 3 * 2';
// 式を通常の PHP コードとして処理する
eval('結果 = @(' . $query . ');');
// 式による解を持つ変数のリストアップ
echo $result; // 11をプリントします。

この仕掛けは、eval()関数が、あたかもPHPコードの文脈にあるかのように文字列を実行することにある。クレイジーだけど、うまくいくんだ。ラッパーはエラーメッセージを抑止します。

より複雑な入力への対応

eval()による式の処理は非常に危険である**ことに加え、誰もが納得するような雄弁な構文が用意されているわけではありません。ユーザーが1つでも構文違反を犯すと、式全体を処理することができなくなります。

したがって、解決策としては、まず形式的な側面からユーザーの問い合わせを理解して修正し(正規化するといいます)、さらにそれを通過させて処理します。

私は過去にまさにこのタスクのためにQueryNormalizerをプログラムしたことがあります。

異なる文脈を正しく理解する必要があるため、処理そのものは非常に難しい作業です。例えば、括弧はネストしたブロックを表し、再帰的に評価する必要があること。例えば、「5+2^(1+3/2)`」という式は、まず分数を解き、括弧内の数字に足し、次に整数の累乗を解き、最後に底辺で足さなければならないので、素直に解くことはできない。

この厳しい要求を満たすためにも、式はもはや普通の文字列として扱うことはできず、抽象化のレベルをとる必要があるのです。演算子の優先順位、意味の違い、文脈、再帰、データ型まで扱わなければならないので、要するに数学は演算と数値の関係を記述する一種の言語なのである。そこで登場するのが、クエリトークナイゼーション処理です。

私は2015年から数学のトークン化の問題に取り組んでおり、それ以来、何種類かのパーサーを書いてきました。

これらのうち、現在新しいMathematicatorを動かしているのは、available opensource on GitHubです。

トークン化のポイントは、文字列を**解析し、型がわかっている小さな文字列のグループに分割し、それらをオブジェクト(データ型)に変換することです。変換されたオブジェクトの配列は、巧妙な論理によって、依存関係や再帰性を記述できる二分木に変換される。これは、何百もの可能性があり、ユーザーが非常にクリエイティブなクエリーを行うことができるため、非常に要求の厳しいプロセスである。

トークン配列の主な利点は、例えば実際の計算を行う木をLaTeXに再描画するなど、次の層に非常に簡単に渡すことができることです。

使い方はこのようにエレガントに見せることができます。

$tokenizer = new Tokenizer(/* いくつかの依存関係 */);
// 数式をトークンの配列に変換する。
$tokens = $tokenizer->tokenize('(5+3)*(2/(7+3))');
// これでトークンをより便利な形式に変換できるようになりました。
$objectTokens = $tokenizer->tokensToObject($tokens);
dump($objectTokens); // 型付きトークンをメタデータ付きで返す
// LaTeXへのレンダリング
echo $tokenizer->tokensToLatex($objectTokens);
// デバッグツリーにレンダリングします(非常に高速)。
echo $tokenizer->renderTokensTree($objectTokens);

手続きを見る

計算するときに、手順が表示される**と、どのように計算したかがわかると、かなりのユーザーから喜ばれます。これはプログラマーにとっても有益なことで、少なくとも計算の誤りを簡単に発見し、それに応じてアルゴリズムを修正することができるからだ。これらを自動テストに基づく機械学習と組み合わせると、驚くべきものが出来上がります。

QueryNormalizer` がどのようにあなたのクエリを理解し、データを tokenizer に渡し、それに従ってクエリを LaTeX にレンダリングし、そしてオブジェクトツリーを計算機に渡して全体の結果を返しているのかを見てみましょう。

Příklad: 5+2^(1+3/2).

手続きの表現は、計算機が入力木を走査し、そこに含まれるトークンやルールに従って一度に1つのルールを評価することで実装される。いずれかのルールが評価されると、そのステップ情報が配列に格納される。時には、あるステップが正しくないことが判明し、計算を戻して別の道を歩まなければならないこともありますが、その背後にはかなりのマジックが隠されているので、今は隠しておいて、実装で勉強してください。

結論

上記の手順は、数値、演算、そしてそれらとの関係を持つ数式をエレガントに扱う方法を説明するものである。この方法では、例えば、式の修正や方程式の解を求めることはできませんが、それは次回以降に見ていきましょう。

その他、効率的な数学の処理方法についてアイデアがあれば、ぜひ教えてください

Jan Barášek   Více o autorovi

Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.

Rád vám pomůžu:

Související články

1.
Status:
All systems normal.
2024