nashcft's blog

時々何か書く。

JavaScriptのRegexとそれの効率的なコードの書き方

これは過去にQiitaに投稿したもの


High Performance JavaScriptに書かれているJavaScriptにおけるregular expressionの書き方に関するメモ。
Regexpの処理に関しては他言語と共通かも判りませんが。

結論

  • Backtrackingを起こさないような作りにしよう

Backtracking

解析中にある文字でマッチングに失敗した際、直近の処理の分岐点まで遡る事。
遡った後、まだマッチングが行われていない選択肢(e.g. 次の文字、|で隔てられた右側の候補)から処理を再開する。

quantifier/alternationに関する処理の流れ

  • quantifier: *, +?, {n}など
  • alternation: |

/h(ello|appy) hippo/.test("hello there, happy hippo")

  1. 1文字目hがマッチする。それ以降の文字に対するマッチングを開始する。
  2. alternationによる候補(左の候補から順に試行を行う)elloとのマッチングを行う。
  3. elloまでマッチするが次の文字(h vs t)でfail。backtrackしてalternationの分岐に移動し次の候補appyaからマッチングを始める。failなのでbacktrack。
  4. 2文字目以降とregexp先頭のhとのマッチングを繰り返し、14文字目のhがマッチ。2.と同様分岐からelloとのマッチングへ。
  5. e vs aの時点でfail。backtrackして分岐に戻り、次の候補appyとマッチング開始。
  6. そこから先が全てマッチングに成功し、happy hippoとマッチ。

greedyとlazy (nongreedy)マッチングの処理の違い

// 例文
var example = '<p>a paragraph.</p>';

// greedy
/<p>.*<\/p>/.test(example);
// lazy
/<p>.*?<\/p>/.test(example);

greedyの場合、.*でのマッチングでa paragraph</p>まで全て検査し、その後残りの<\/p>とのマッチングの為に1文字ずつbacktrackして.*に含まれる文字を減らしながら検査していく。
lazyの場合<p>までマッチングした後、.*?と空文字がマッチした状態から<のマッチングを開始し、対象の文字列中の<にマッチするまでbacktrack->1文字.*?のマッチ対象に含めながら1文字ずつ検査を進めていく。
マッチさせたいパターンからbacktrackingの回数を求め、適切な方を選ぶ。

不要なbacktrackingを防ぐ

トークンをオーバーラップさせない

隣り合ったトークンが対象についてオーバーラップしないようにする。
例えば適当な文章から""で括られている文をマッチさせたい時に/".*?"/ではなく/"[^"\r\n]*"/を使うとか 。前者では."による対象文字列中の"へのオーバーラップが生じ、backtrackingが起こる分岐点が発生している。後者にはオーバーラップが無い。

Lookaheadを使う

Lookaheadとは: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#special-lookahead JavaScriptには無いatomic groupingの機能を模倣する事が出来る。
atomic groupにマッチした文字列はbacktrackingで検査が再開されるポイントにはならないので余計な検査が行われない。
Lookaheadは通常その中に含まれるregexpをマッチ対象として扱わない(上記リンクに挙動の例があります)が、以下の様にregexpをcapturing groupに包んで、lookaheadの外にバックリファレンスを与える事でマッチした対象として扱う事が出来る。

/...(?=(pattern))\1.../

使いどころとしてはmarkup言語に対して検査を行う時。

分岐箇所を極力小さく

複数単語のマッチングに対してalternationで単語ごとに分けるのではなく異なる部分だけ切り出す感じ。
以下例

instead of use
cat | bat [cb]at
red | read rea?d
red | raw r(?:ed|aw)
(. | \r | \n) [\s\S]

補足としてcharacter class ([\s\S]とか) を使った方がエンジンの実装的にalternation使うより速いっぽい。

ベンチマークの方法

backtracking捕捉の為、作ったregexpに対して大部分がマッチするけどマッチしないパターンを含む長い文字列を使う。

そもそも使わない

検査対象が非常に具体的な場合はString型のメソッドで処理した方が安上がりだったりする。

その他

シンプルに、具体的に、明示的に、等々

第15回Solr勉強会 メモ #SolrJP

特に前知識など無しに参加してみた。技術調査的なノリだった。

Lucene / Solr Revolution 2014 Report : 株式会社ロンウィット 打田さん

  • Lucene FST (Finite State Transducer)とSolrTextTaggerの紹介。
  • conferenceに関してはスライドを読んでくださいとの事。
  • 多言語対応やsemantic searchのアーキテクチャについてのセッションがあった。
  • 多言語対応に関してはプレゼンの際には省略されたが、言語の扱い方を3つの戦略に分け、それぞれのPros/Consを挙げている。
    • 言語毎にfieldを分ける場合、実装がシンプルになるが対応言語数が増えるにつれてパフォーマンスが落ちるので何かしら手を打つ必要が出てくる。
    • 言語毎にcollection / coreを分ける場合、言語数に関わらずスピードが出て、検索対象となるfieldは1つなのでパーサの選択何でも良いが、環境含め複雑さが増す。
    • 全ての言語を1つのfieldに入れる手法はSolrには機能として派存在しないらしい。何か自分で書くっぽい。
  • Semantic search = query augmentation (再現率の向上) + ranking tuning
    • 入力された検索ワードに対して類似の単語や付加情報を追加し、重み付けを加えたものを実際の検索に用いる。
  • 拡張は以下の4ステップで行われる:
    1. ドメイン特化したフレーズモデルの作成 (machine learningなどを利用)
    2. SolrTextTaggerに1.で作成したフレーズを食わせる
    3. SolrTextTaggerで検索クエリからフレーズ抽出、tagging
    4. クエリを書き換える
    5. 1.のMLに関する所がセッションではあまり話されなかったと言っていたが、Lucene / Solrにがメインのconferenceだからそんなものでは?
  • SolrTextTaggerはLucene/Solrを応用したフレーズタガー。2つの単語認識とフレーズ認識の2段階でFSTを用いている。
  • このFSTの実装がLuceneFST
    • Finite State TransducerとはFinite State Automataが受理時に何かしらの値を出力する機能を持ったもの (?)
      • でもSTTでは出力を使ってないらしい
    • "Essentially, an FST is a SortedMap<ByteSequence,SomeOutput>, if the arcs are in sorted order."

  • これを使ったらLuceneのFuzzyQueryが100倍速くなった(!)
  • 応用として自然言語処理における高速・省メモリ辞書に使えるかも。
  • デモ、図説、etc...

Read more

Relational Theory for Computer Professionalsを読んだ

Relational Theory for Computer Professionals: What Relational Databases Are Really All About (Theory in Practice)

Relational Theory for Computer Professionals: What Relational Databases Are Really All About (Theory in Practice)

すでにだいぶ前の話ではありますが。
大学時代NoSQLはそれなりに追っていたのですがRDBはそうでもなかったので、一度理論側からしっかり勉強してみようと言う事で、1冊目にこの本を選びました。
200ページそこそことそれほど分量もなくさくっと読めるかと思ったら結構手こずりました。その辺に関しては後ほど。

構成としては3つのパートに分かれており、パート1がrelational modelについて、パート2はデータベースのトランザクションとデザインについて、パート3はSQLについて述べられています。Relational modelのoperationについてはTutorial Dで、SQLはISO標準に基づいて記述されています。パート1とパート3を対応させるように書かれており、理論と実装の差異がより明確に浮かび上がらせ、この対比を通してrelational theory本来の簡潔さや表現力の高さを主張しています。
AppendixではTutorial Dの補足的な解説や定義について、また集合論のちょっとしたまとめが付随してるので、数学的な背景に自身の無い人でも必要最低限は押さえられると思います。

内容とは関係ないですが、とてもwordyな本だという印象を持ちました。もっと簡潔に書いてくれたらもっと評価高かったんだけどな...。
あとDate氏はSQL嫌いなんだろうなーと思いました。Prefaceののっけから"... considered as a concrete realization of the abstract ideas of the relational model, SQL is very deeply flawed" なんて言ってますし。その後も SQLは全然relationalじゃないよーと度々主張していたり、パート3の最終章ではその具体例を列挙していたり。

RDBやそのoperationの集合論的な捉え方をある程度習得できたし、よりrelationalなSQLの書き方も解った所で、さて次は何を読もうか。

TortoiseHg (+Mercurial)のmerge toolやdiff toolがちゃんと使えるようになるまで

ひょんな事からLinux上でTortoiseHgを使う事になったがdiff toolやmerge toolにgvimdiffを設定した所上手く起動せず、二進も三進もいかなくなってtwitterでぼやいたら入門TortoiseHg+Mercurialの著者フジワラさん(@flyingfoozy)を始め複数人の方に助けていただいたので、教えていただいた内容をまとめます。
なおこの記事は私のケースと同じくgvimdiffを設定する場合のみ扱っています。より一般的な情報についてはフジワラさんによる解説記事を参照してください。

Read more

brew導入でwarningやら何やら躓いたのでまとめ

MacにArangoDBをインストールしようと思ったら何やらHomebrewからどうぞ!*1みたいな感じだったので、そういえば以前インストールしたきりだったし折角だから使ってみよう、と動かしてみた所幾らか引っかかった事がありました。とりあえずそれらを簡単に纏めてみます。
備忘録として残す為に書いており、結構アホっぽい事やってると自分でも思うので適当に読み流していただければと思います。

*1:パッケージを直接ダウンロードする事も出来る

Read more