[ アカウントをゲット! ]
正規表現より文脈自由文法の方が良いという意見に対する正規表現の擁護
「正規表現が手軽だから」以上が無くて、言語の表現力の話とリテラルとしての正規表現の話がごっちゃになってる気がする。この論法だと、正規表現から上位のクラスの言語用のパーサへ変換する機能を用意されたらそれでいいことになってしまう。
スクリプト言語の機能としての正規言語マッチャは、使う側としては、正規言語(+α)に限れば圧倒的に速くパースできるという以上のメリットは無いと思う。「チューリングマシンが最強」というのは確かで、パーサコンビネータとか手書き再帰下降パーサとかは0型の文法でもパースできて最強だと思う。速度以外は。
もちろん研究者としては機能が制限されてる方がいろいろ性質を保証できたり解析できたりして嬉しいけど、スクリプト言語を使う側にとってそれを生かした便利な機能って、速度とか停止性ぐらいしか今のところないんじゃないだろうか。
速いから正規言語に限るんであって、速度を犠牲にしても良いんなら上位のクラスの言語用の機能だけ用意して、正規表現からそれに変換すればいいと思う。
追記:
見直してみたら、元から正規表現対BNFの話のようです。話をごっちゃにしてたのは私の方だったようです。
こんなん作った。hello.rb
これは、
foo1 = procedure do foo(1, _, _) end
と書くと
foo1 = proc do |x, y| foo(1, x, y) end
とほぼ同じことになる。
途中で例外が起きたり、callccを使うと変になるはず。
あと、
procedure do 2.times do p _ end end
は1引数手続きではなく2引数手続きになって、_の値は1回目のループでは1つめの引数、2回目のループでは2つ目の引数になる。
shift/reset言いたかっただけなので、実はもっと簡単に書ける。
def procedure
proc do |*args|
old_index = Thread.current[:index]
old_args = Thread.current[:args]
Thread.current[:index] = 0
Thread.current[:args] = args
begin
yield
ensure
Thread.current[:index] = old_index
Thread.current[:args] = old_args
end
end
end
def _
Thread.current[:index] += 1
Thread.current[:args][Thread.current[:index] - 1]
end
これは例外には対応しているけど、やっぱりcallccに対応していない。
『ジェネレーティブプログラミング』という本を読んでいる。といっても前半は飛ばしたし、後半もあまり読めてないんだけど。
そこで構成レポジトリというのがでてきておもしろい。
例えば行列計算ライブラリを作るとする。
行列は要素の型がfloatだったりdoubleだったり複素数だったり、行列で使うベクトルの型も行列の種類によって違ったりして、その組み合せは膨大になる。
そのため、C++ならテンプレートを使って、行列クラスは要素の型やベクトルの型を受け取るようにするとよい。
つまり、
template<class ElementType, class VectorType>
class Matrix {
(ElementTypeやVectorTypeをここで使う)
}
みたいな感じ。
このとき、要素の型とベクトルの型を両方いちいち指定するのはめんどうなので、まとめてどこかに1つだけ書いておいて、それを参照するようにできると便利。
それが構成レポジトリと呼ばれるものである。つまり、
struct FooConfigrationRepository {
typedef double ElementType;
typedef FooVector<ElementType> VectorType;
}
と書いておいて、
template<class Config>
class Matrix {
(ElementTypeの代わりにConfig::ElementTypeを使う)
}
という風にMatrixの方を書き換えておけば、FooConfigrationRepository1つを渡すだけで済むので楽だし、後で変更するときもここだけ変えればいいので楽になる。
というのが構成レポジトリのごく簡単な説明。
ただ、『ジェネレーティブプログラミング』ではC++での例は載っているけど、Javaの例は載っていない。
そもそもJavaではクラス内でtypedefできないのでC++と同じようにはできない。
しかし、JavaでもGenericsを使えば似たようなことはできる。
まず、構成レポジトリは次のように書く。
interface ConfigrationRepository<ElementType extends ElementInterface, VectorType extends VectorInterface<ElementType>> {
VectorType createVector(int size);
...
}
class FooConfigrationRepository implements ConfigrationRepository<Double, FooVector<Double>> {
public FooVector<Double> createVector(int size) {...}
...
}
それで、受け取る方は次のように書く。
class Matrix<ElementType extends ElementInterface, VectorType extends VectorInterface<ElementType>, Config extends ConfigrationRepository<ElementType, VectorType>> {
(ElementTypeやVectorTypeを使う)
}
それで、これらのライブラリを使う場合には次のようにする。
class Main {
public static void main(String [] args) {
start(new FooConfigrationRepository());
}
<ElementType extends ElementInterface, VectorType extends VectorInterface<ElementType> Config extends ConfigrationRepository<ElementType, VectorType>> void start(Config config) {
Matrix<ElementType, VectorType, Config> bar = new Matrix<ElementType, VectorType, Config>(config);
...
}
}
つまり、例えばFoo<Double, Integer>という型を、2つの型DoubleとIntegerを要素として持つレコードとして使っている。
そこから要素を取り出すときは、<X, Y, Z extends Foo<X, Y>>という風にパターンマッチを使って取り出している。
ただ、C++に比べて記述はかなり汚なくなっていてあまり実用的ではないし、ここまで凝ったことはJavaではやらないのがJavaの流儀っぽい気がする。
# JavaのGenericsっていままで詳しく調べたことなかったので、実はもっとスマートな方法があるかもしれない。
スーパーマリオブラザーズに対するツッコみとして、レンガに変えられたキノコ王国住民を破壊するのはどうよ、ってのがよくある。
しかし、説明書には次のような記述がある
レンガに変えられたり、消されたりしたキノコを見つけて助けると、彼等からパワーをもらって、次々と変身します。
ここから考えると、壊れるブロックはただのブロックで、叩くとアイテムが出るブロックがキノコ王国住民なんじゃないだろうか。
消されたりしたキノコってのは隠しブロックのことで。
今までOrbit Optical使ってたんだけど、ボタンがへたってきたのでポインティングデバイスを買い替えることにした。
またOrbit Optical買ってもいいんだけど、もうちょっと冒険してみようと思って、空中マウスの一種であるMX Airを買ってみた(腕のねじれないデバイスが欲しかった)。
で、繋いでみたところ、専用ドライバの無いLinuxでもある程度は使えた。以下詳細。
SELが左ボタンと同じイベントなのと、再生/一時停止ボタンやVOLボタンが認識されてないのが痛い。それ以外はまぁまぁ。
さらに自作ツールでevdevを調べてみたところ、以下のイベントを発生させると宣言している。
Event type 0x00 (Sync Events)
Report Config
Event type 0x01 (Keys or Buttons)
Mouse Button Left Mouse Button Right Mouse Button Middle Mouse Button Side Mouse Button Extra Mouse Button Forward Mouse Button Back Mouse Button Task Unknown 0x118 Unknown 0x119 Unknown 0x11a Unknown 0x11b Unknown 0x11c Unknown 0x11d Unknown 0x11e Unknown 0x11f
Event type 0x02 (Relative Axes)
X Y Horizontal Wheel Wheel
以下追記
音量調節ボタンなどは別のデバイスファイルに割り当てられる。それらは以下のイベントを出力すると宣言している。
Event type 0x00 (Sync Events)
Report Config
Event type 0x01 (Keys or Buttons)
Mute Volume Down Volume Up Pause Stop Props Undo Copy Paste Find Cut Help Calc Sleep File WWW Mail Bookmarks Back Forward Eject CD Next Song Play/Pause Previous Song Stop CD Record Rewind Config Homepage Refresh Scroll UP Scroll DOWN Unknown 0xb5 Unknown 0xb6 Play Fast Forward Bass Boost Print Unknown 0xe7 Unknown 0xe8 Unknown 0xe9 Unknown 0xea Unknown 0xeb Button 0 Program Radio Channel Up Channel Down
Event type 0x02 (Relative Axes)
Horizontal Wheel
Event type 0x03 (Absolute Axes)
Volume value 0 minimum 1 maximum 652 fuzz 0 flat 0
同梱されているWindows用のユーティリティー(SetPoint)で設定した設定はハードウェアに保存されるわけではないらしく、Linuxでは有効にならない。ただ、前述のプログラムの出力を見ると、キーのカスタマイズはソフトウェアによる変換ではなく、ハードが直接別のイベントを出力しているのだと予想できる。おそらく、なんらかのコマンドを送るとハードウェアが別のイベントを送信するようになるのではないかと思う。
Firefox用拡張でRadialContextというのがあって、これを使うと普通のコントキストメニューの代わりにドーナツ状のパイメニューがポインタの周りに出るようになる。
素晴しいのはポインタをドーナツの外に動かそうとしてもちゃんとメニューがポインタに付いてくる点で、マウスジェスチャーの感覚で使える。しかもマウスジェスチャと違ってアイコンが表示される上、ポインタを動かさないでいると説明テキストが出るので、割り当てを忘れてしまっても大丈夫。
これがあるお陰で私は他のブラウザを使えない。
だけど、けっこう前からしばらく使っていると突然メニューが動かなくなって、しかもマウスやキーボードのイベントをグラブしたままになるのでFirefoxがまともに使えなくなくなってしまう。
これじゃ使いものにならないってんで調べてみたところ、とりあえず対症療法が見付かった。
rcMenu.jsでラベルのfontWeightを設定しているところでエラーが出ていた。
設定しているところは幾つかあってその内1つでは
try {...} catch(ex) {dump("Why does setting the font weight fail sometimes?\n" + ex.toString() + "\n");}
という風に例外をキャッチしているが、その他の部分では例外をキャッチしていない。これが原因っぽい。
また、showLabelsの
this.lnode.style.lineHeight= 20*100/currentZoom+ "px";
this.lnode.style.fontSize= 8*100/currentZoom+ "pt";
の部分でも例外が起きている。
とりあえず例外が起きている部分を、try...catch...で囲むと、今のところ一応動いてる。
エラーメッセージにあるように、この例外がなぜ起きるのかは謎なので根本的な解決にはなってない。
おまけ:
application/xhtml+xmlなページでパイメニューが表示されないのは、rcIntegration.jsの
pie_context.body= pie_context.frame_doc.getElementsByTagName("BODY")[0];
というところで大文字の"BODY"を指定してるのが原因っぽい。その後に
if (!pie_context.body) {
pie_context.body= pie_context.frame_doc.getElementsByTagName("body")[0];
}
を入れてみると表示される。
グラフィカルなCLI系でまた1つ。
あと、類似プロジェクトのリストが参考になる。けっこうあるもんだな。
GUIエディタよりコマンドラインエディタが本質的に優れているなどという理由では、決してない。
(既存の)GUIの設定ツールによる管理の悪い点はコピペしたりできない点。個々のテキストフィールドはコピーできるけど、設定画面全体のコピペはできない。
GUIもコピペしたりファイルに保存できればいいのに。と思ったけど、「それなんてxfy?」だな。
ところで、元エントリは一般的なGUI vs CLIの話なのか、viという具体的なテキストエディタの良し悪しの話なのか、専用管理ツールによる管理 vs 汎用テキストエディタによる管理の話なのか読み取れない。
以前から言っているグラフィカルなコマンドラインインターフェースは、入力はテキストで、出力はグラフィックスというものだった。
しかし、コマンドラインが敬遠される理由としては、学習コストの高さが挙げられる。実際私もLinuxのコマンドの細かいオプションまでは覚えきれない。
そこで、入力の方にもグラフィカルな、入力の補助インターフェースを付けてみてはどうかと思い、例によって絵を描いてみた。
ムービーの再生が終了して、grep的なコマンドを入力しているところという設定。
以下説明兼アイデアメモ
ただ、このインターフェースはプログラマーに負担をかけるので良くない。古き良きUNIX的な環境を作りたいのにWIMPを使ったGUIを構築しないといけないのは良くない。
ちなみに、入力がテキストで出力がグラフィカルなやつはFirefoxの拡張としてプロトタイプを作成中。Firefoxの拡張作るのめどい。
XULはインターフェースだけで、シェルとしての処理はRubyで書いたサーバーで実行。今のところ、dateと入力するとカレンダーが出力される程度。将来的にはコマンドを任意の言語で書けるようにしたい。パイプの中はMultipart風な構造でJSONのストリームが流れる予定。
PowerShellみたいにコマンドを実装する側にいろいろ制約をかけてしまうのはありだけど、あんまりやりたくない。それで得られるものもあるからある程度は制約をかけないといけないんだろうけど、ほとんどのプログラミング言語で、特別なライブラリ無しでもコマンドを書けるようにしたい。
もし公開する場合はシェルとしてではなくファイラーというカテゴリで出す予定。そっちのほうが使う側の心理的な障壁や要求が低いイメージがあるので。
このページのすべての商標と著作権はそれぞれの所有者が有します。
コメントやユーザ日記に関しては投稿者が有します。
のこりのものは、© 2001-2010 OSDN です。