/ 最近 .rdf 追記 設定 本棚

log[javascript: 2008-10-24]



20081024() [HTML] Logicoolのサトはキーボドでもタブの切り替えができて偉い(スクリトをOFFにするとマウスを使ってさえ表示を切り替えられなくなったが…)

[HTML][javascript] 補助に徹する賢い JavaScriptの使い方

基本はこう

  1. HTMLのみで情報を配置する
  2. CSSで見栄えを整える
  3. スクリトで効果やアクションを付け加える

Logicoolのサトのタブコトロールはキーボドインターフェイスを具えていて偉いと書いたばかりだが、スクリトがオフだとタブの切り替えができないばかりかタブの中身、肝心の情報が表示されないのはイクナイ

スクリトで各タブの表示・非表示を切り替えているのだろうが

  • タブの状態のデフトは非表示
  • スクリトで一つだけを表示に切り替え

ではなく

  • タブの中身はデフトで表示
  • スクリトで一つ以外を非表示に切り替え

より良いやり方ではない(一瞬全ての情報が表示されてすぐに消えるというのは気にすることではないと思ってい)

ロジのサトのこのスクリトは見栄えをいじるだけなのでHTMLとスクリトを切り離して最初に HTMLのみでページを作りスクリトは後からアタッチするものだと考えて欲しかった

スクリトに依存する要素があって切り離せない場合はAmazon.co.jpがやっているように

  • <noscript>の中にスクリトを使わない代替 HTMLを用意する (できなければないで仕方がな)
  • <script>の中でスクリトを必要とする要素をスクリトを使って配置する

方法が賢いと思っている

 っとだけ関連


20080527() [Firefox] 領域を選択してのソース表示はスクリトに書き換えられた最新の HTMLを反映しているのが便利

[Firefox][javascript][SHJS] <pre>が真っ白になり黒色の領域が出現する

例えばこのページ http://vvvvvv.sakura.ne.jp/ds14050/diary/20080112-7.html Endーで末尾に移動して PageUpで戻っていくと空白の PREが目に入ると思うその少し上にはページの内容を覆い隠す黒い領域があるはず。(そうでなければ修正されたのだろうFirefox2で最初に確認しFirefox3.0RC1でも直っていなかった)

大量の PREが存在したり一つだけでも巨大な PREが存在する場合に起こる様子innerHTMLPREの内容を置き換えているのも原因になっているかもしれない

画面の末端にスクロールした状態でページをリロ(F5 or Ctrl+R)すると下方の PREが正常に表示される反面上端付近の PREに同じ問題が生じる遠方の PREの書き換えに問題があるのでは?

っ白の PREの中で右クリックしたりテキトを選択したりといったアクションを起こせば正常に表示されることが多い


あとPREの中から開始した選択は PREの外に出られなかったり(これは TEXTAREAと違い PREでは Ctrl+Aで全文選択ができないために用意された代替手段だという気もす)


20080514() DFAエンジンのマッチの仕組みは謎のまま残された正規表現を利用する側からはコトロールできる部分が皆無で常に同じ結果が返ってくるおもしろみのないものらしいけどその魔法の実現方法は大いに知りたい

[正規表現][javascript][大型] Jeffrey E.F. Friedl【詳説 正規表現 第3版】 オライリージャパン

読んだこの日記で以前書いたようなこと(20080116p01, 20080111p01)は全て書いてあったもちろんそれ以上に知らないこと(NFAのマッチングのしかたNFA型正規表現エンジンに適用できる正規表現のチーニングの具体例UnicodeサポPerl, .NET, Java, PHPの正規表現\Gの使い方などなど)が書かれていた

非常に読みやすい文章で書かれているし必要なところでは必ず前後のページへの参照先が書かれている章の始めには Overviewがありその章から読み始めた読者への配慮も忘れない当たり前のことだけど徹底されている「まずこの本を読め正規表現について話すのはそれからだと言い切れる良い本正規表現を初めて学ぶ人にも効率について考える余地ができてくるほど既に正規表現を使っている人にも役に立つ

すごく実用的なテクニックででも全く想像が及ばなかったものがある168ージ4.5.8.1 肯定の先読みを使ったトミックグループの模倣がそれ

 肯定の先読みを使ったトミックグループの模倣

/(?>pattern)/     // アトミックグループを使ったパターン
/(?=(pattern))\1/  // 先読みでアトミックグループを模倣したパターン

高機能化する他の実装にくらべて昔のままの JavaScriptの正規表現はバトラックを抑制する構文を持っていないJavaScriptでは非常に有用

20080116p01でも書いたが次の終わらない正規表現

/"(?:[^\\"]+|\\.)*"/       // マッチに失敗するとき死ぬほど遅い

トミックグループや絶対最大量指定子が使えるなら次のように書けるが JavaScriptは両方ともサポトしていない

/"(?:[^\\"]+|\\.)*+"/      // JavaScriptでは使えない
/"(?>(?:[^\\"]+|\\.)*)"/g  // JavaScriptでは使えない
/"(?:[^\\"]++|\\.)*"/      // JavaScriptでは使えない。※上2つとは少し意味が違う

次のように先読みでトミックグループを模倣すると組み合わせの爆発を避けることができる

/"(?=((?:[^\\"]+|\\.)*))\1"/
/"\1"/            // 上のパターンから先読み部分を取り除いたもの。

先読みを取り除いたパターンを見ると一目瞭然だが引用符がペアになっていなくて \1 の後ろの " のマッチに失敗したとしても戻る場所がないあるのは "\1 にマッチしたという結果でどちらもオプションではないので取り消すことはできず繰り返しでもないのでマッチした部分を少しずつ手放させることもできないなのでっとずつ後じさりしながら延々とあらゆる組み合わせのマッチを試行することなしにッチが失敗に終わったことが即座に判断できるようになるというわ本物のトミックグループよりは劣るが効率も悪くない同じ働きをする次の二つのパターンとかかる時間を比較してみた

/"[^\\"]*(?:\\.[^\\"]*)*"/
/"(?:[^\\"]|\\.)*"/

 手順

トラックによる組み合わせの爆発が起きない 3つのパターンでかかる時間を比較3回実行した(3回繰り返しても一回一回の中の試行順が固定されていたら傾向は同じになるわな無意味あてみやむい)

var re = [
	/"(?:[^\\"]|\\.)*"/,
	/"(?=((?:[^\\"]+|\\.)*))\1"/,
	/"[^\\"]*(?:\\.[^\\"]*)*"/
];
var s = [
	'"'+ new Array(5000+1).join('\\"'),        //  1/100
	'"'+ new Array(500000+1).join('\\"') +'"',
	'"'+ new Array(500000+1).join("\\'"),
	'"'+ new Array(500000+1).join("\\'") +'"',
	'"'+ new Array(500000+1).join('a'),
	'"'+ new Array(500000+1).join('a') +'"'
];
var results = [];
for(var j = 0; j !== s.length; ++j) {
	var result = [];
	for(var i = 0; i !== re.length; ++i) {
		var t0 = new Date();
		var m = re[i].exec(s[j]);
		result[i] = new Date() - t0;
	}
	results[j] = result;
}
WScript.Echo(results.join("\n"));

 結果

数の単位は msec

パターン1パターン2パターン3
工夫なしトミックグループの模倣ープ展開
/"(?:[^\\"]|\\.)*"//"(?=((?:[^\\"]+|\\.)*))\1"//"[^\\"]*(?:\\.[^\\"]*)*"/
文字列1ッチしない(F)"\"\"......\"\"2910×100, 2928×100, 2914×1002551×100, 2581×100, 2595×1002372×100, 2387×100, 2377×100
ッチする(T)"\"\"......\"\""124, 124, 124138, 137, 134108, 107, 108
文字列2ッチしない(F)"\'\'......\'\'138, 140, 151125, 127, 125122, 118, 118
ッチする(T)"\'\'......\'\'"138, 126, 126140, 128, 133135, 105, 106
文字列3ッチしない(F)"aa..........aa174, 172, 16614, 11, 1396, 90, 92
ッチする(T)"aa..........aa"155, 119, 12632, 15, 1415, 12, 11

 みどころ

  • ッチに失敗するときの成功するときに比べた遅さ
    • パターン2は例外
  • パターン2(トミックグループの模倣)ではしばしばマッチに失敗する方が速い
    • \1のマッチが成功だと判断するにはキャプチャした長い長い文字列を最後までたどって比較する必要があるため\1のマッチに失敗するほうが速くなる?
  • 文字列1Fの特筆すべき遅さ
    • 遅いとはい「終わらないと形容するほど遅くはない(これでも)
    • 文字列長に比例したバトラックが行われているせい?
    • 文字列2Fの結果と比較するに\" という形で " が文字列の途中に含まれていることが最適化を阻んでいる?
  • パターン3(ープ展開)は特定の場合を除いてパターン2(トミックグループの模倣)より若干速い
    • ープ展開『詳説 正規表現に載っていた言葉
    • 特定の場合とは文字列3Fのことで不用意なパターンを用いると処理が終わらなくなる場合のこと
  • パターン2(トミックグループの模倣)(今回の眼目である)組み合わせの爆発が起こるような場合に顕著な速さを見せる
    • 他の文字列ではパターン3(ープ展開)に半歩譲る

ところで文字列1Fがどのパターンでも一様に遅いのは文字列長に比例したバトラックが行われているからなんだろうがパターン2(先読みによるトミックグループの模倣)でもそれを抑制できていないのはなんとかできないものそれでこそ若干のオーバーヘドをのんででもトミックグループの模倣を採用する理由になるのだ


20080221() エクスプローラはバカだエクスプローラはバカだエクスプローラはバカだdesktop.iniは目障りな上に役に立っていない(C:\Windowsが音楽フォルダって何? 実体のないフォルダ(マイコンピータごみ箱デスップーザ)の表示設定も保存し)

[javascript] JavaScript空文字列にまつわる微妙な点 (String.split(), RegExp.lastIndex)

 空文字列を split()

split()の第一パラメータ separatorが空文字列にマッチするかどうかで結果が異なる

"".split(" ").length;      // 1 (空文字列にマッチしないから)
"".split("").length;       // 0 (空文字列にマッチするので)
"".split(/\s+/).length;    // 1 (空文字列にマッチしないから)
"".split(/^$|\s+/).length; // 0 (空文字列にマッチするので)

function getClasses(element) {
  return element.className.split(/^$|\s+/);
}
 追記@2008-05-28: 空白で始まったり空白で終わるときのことを考えていなかった

上の functionでは classNameが空っぽの時には空文字列の要素を作らないが頭や尻尾に空白が付いていると空文字列の要素が残る (IEを除いて)事前トリミングする手間をかけるくらいなら一個二個の空文字列を気にせず(だけど連続する空文字列の要素は気にして) className.split(/\s+/) とする方が好みだな

 空文字列にマッチした後の lastIndex

IE7Firefox2で異なるFirefox2の方が正しいが無限ループに陥りやすい

var re = /\b/g; // 単語境界にマッチする、幅0のメタ文字。
var str = "012 456 89A";
re.lastIndex = 0;
for(var i = 0; i !== 5; ++i) {
  alert("("+ re.exec(str).index +","+ re.lastIndex +")");
  // IE7: (0,1) (3,4) (4,5) (7,8) (8,9) ...
  // Fx2: (0,0) (0,0) (0,0) (0,0) (0,0) ...
}

空文字列にマッチしていれば(IE7でスキップされるマッチがでてく) exec()の前後で lastIndexの値が変わっていなければ(Fx2でのマッチ回数が IE7より増え) indexlastIndexが同じならば lastIndex1インクリメとしておくとどちらでも間違いが起こらない

var re = /\b/g;
var str = "012 456 89A";
re.lastIndex = 0;
for(var i = 0; i !== 5; ++i) {
  var index = re.exec(str).index;
  alert("("+ index +","+ re.lastIndex +")");
  // IE7: (0,1) (3,4) (4,5) (7,8) (8,9) ...
  // Fx2: (0,0) (3,3) (4,4) (7,7) (8,8) ...
  if(index === re.lastIndex) {
    ++re.lastIndex;
  }
}

ープで

  if(index === re.lastIndex) {
    ++re.lastIndex;
  }

なんて分岐を増やすより文字列末尾にマッチする /$/ を例外として*正規表現から空文字列にマッチする可能性を排除する方が良さそう

* /$/.exec("str") の後の lastIndexプロパIE7Fx2とも最後の文字の次を指す。


20080215() [かな変] F6にかえてCtrl+U[カナ変] F7にかえてCtrl+I[部分確定]↓にかえてCtrl+N[辞書検索/辞書ページ送] End[電子辞典切替メニ] Ctrl+End

[javascript]Re: Javascriptstring型とbooleanoneOfメソド他 (agenda)

コメト欄がないのでここでひっそりつっこんでおきます。

 それは valueOf()

StringBoolean(と NumberDate)だけこのように比較方法を === から == にしたりする必要はなくて

String.prototype.equals = Boolean.prototype.equals = function(arg){
    return this == arg;
};

こう

Object.prototype.equals = function(that) {
  return this.valueOf() === that.valueOf();
}

これでオブジトに関しては同一性をチックしプリミブでは値が等しいかどうかをチックする

ところで配列の場合

valueOf メソドの動作はオブジトの種類に応じて異なります

配列の要素はカンマによって連結される文字列に変換されます。Array.toString メソドや Array.join メソドと同じように動作します。

 Microsoft JScript 5.5 リファレンス (chm)

と書かれていて同一性のチックができなさそうに思えるけどJScript5.7Firefox(javascript1.5)ともにチックできていた

 追記

Array.prototype.valueOf()StringBooleanNumberDateのそれと違って特別に用意されてはいなかった

: 15.4 Array オブジ(Array Objects)

ということは Object.prototype.valueOf()が呼ばれるはずでこれは thisを返す。

var a = [1];
alert(a.valueOf() === a); // true

JScript5.5のいいかげんなリファレスを書いた人出てきなさい(はじめての言語だった JScriptHSPのヘルプには変数の概念から始まる丁寧な説明を受けました大変感謝していま)


20080212() エクスプローラがメモリを放さないプライベトワーキングセトは 14MBでもVistaより前のタスクマネージャが表示していた数字では 215MBになる仮想メモリ不足ってメッセージが何度も出るんですけど

最終更: 2010-03-21T03:27+0900

[javascript][SHJS] JSLint <http://www.jslint.com>

SHJSsh_main.jsを高速化したことを以前書いた(20080204p01)

 対応ブラウザ

SHJSのページには動作を確認したブラウザとして以下が挙げられている

  • Firefox 2
  • Internet Explorer 6
  • Internet Explorer 7
  • Opera 9
  • Safari 3

sh_main.jsの修正版は Firefox2IE7Opera9で正しく動作することの確認と速度の比較を行っている

IE6での確認は IE7から戻すのが面倒なので省略する

Sarari3Vistaで動くものがダウンロドできるので確認してみたところ動いた(表示も正)

いじったことで対応ブラウザが減っていなくて良かった(IE6)

 JSLint

SHJSの作者は Code Conventions for the JavaScript Programming Languagejslint: The JavaScript Verifier かそれに類似した文書を読んでいるに違いない(これらのページを今日発見し)

というのもsh_main.jsJSLintでチックしてみたがこういうエラーしか出なかった

Error:

Implied global: document 362, sh_languages 347, window 332

このエラーは JSLint向けにコメトを埋め込めば取り除けるものだしそうしないと不可避だともいえるもの

 さてさて自分がいじったことでどれだけお行儀の悪いスタイルが混入したのでしょうか
Error:

Implied global: document 207 360, sh_languages 332, window 329

Problem at line 73 character 48: Use the array literal notation [].

   matchCaches = language.matchCaches = new Array(language.length);

Problem at line 86 character 17: 'i' is already defined.

   for(var i = matchCaches.length-1; i !== -1; --i) {

Problem at line 97 character 22: 'i' is already defined.

   for (var i = state.length-1; i !== -1; --i) {

Problem at line 110 character 17: 'i' is already defined.

   var i = (pair[0] & 0x3F);

Problem at line 280 character 15: Use '!==' to compare with '0'.

   while(0 != this._currentStyles.length) {

Problem at line 389 character 14: 'node' is already defined.

   var node = this.free_;
 いいわけ
  • ーカル変数の再宣言に関するものは全て forープで初期化される変数が原因(inode)
  • var array = new Array(length);」をvar array = []; array.length = length;」やvar array = Array.prototype.slice.call({length:length}, 0); と書き換えることは拒否する

    (new Array(length)が一番簡潔で自然な書き)

    (JavaScript 1.7配列内包に書き換えるのには吝かでな)

  • != 0 の代わりに !== 0 と書け? 書き換えましたm( __ __ )m

(一つを除い) 無視できる警告*ばかりで良かった

* 無視したら警告の意味がないforープの変数なんて(古い VC使い以外には)スコープの誤解を招きやすいという理由で避けなければいけないものの筆頭ともいえる


20080208() 寝ているすきに腕まくらをされに来ていたにゃんここれだから冬は(≧∀)

[javascript] ArrayStringの故意に汎用的なメソドたち (1)

Under Translation of ECMA-262 3rd Edition を読んでいて見つけ「故意にに汎用的であると書かれたメソド群ArrayStringのほとんどのメソドが該当する

FirefoxArray.prototype.methodなどを Array.methodからも参照できるようにする(している)のも 仕様が汎用的で再利用が可能になっているからだろう

それら汎用的なメソドを使い回してやるためにその動作を javascriptのコドで表してみる

 Array.prototype.concat([item1[, item2[, ...]]])

function() {
  var array = [];
  var array_index = 0;
  var item = this;
  var arg_index = 0;
  do {
    if(item instanceof Array) { // (1)
      var item_index = 0;
      while(item_index !== item.length) {
        if(item_index in item) {
          array[array_index++] = item[item_index++];
        } else {
          ++array_index; ++item_index; // (2)
        }
      }
    } else {
      array[array_index++] = item;
    }
  } while(arg_index !== arguments.length &&
          (item = arguments[arg_index++] || true)
  );
  array.length = array_length;
  return array;
}
  • thisと引数は Arrayであるかどうかをチックされ扱いが変わる(1)
  • 新しい配列にコピーするときに(Arrayであ) thisや引数の疎な部分が省略されることはない(2)
  • thisArrayでない場合単に戻り配列の "0" プロパthisオブジトが入っているというだけである(余計な引数なしで呼び出してArrayでなければ配列化という使い方ができるうれしいか)

 Array.prototype.join(separator)

function(separator) {
  var length = this.length>>>0; // (1)-1
  if(typeof(separator) === "undefined") {
    separator = ",";
  }
  separator = ToString(separator);
  if(length === 0) {
    return "";
  }
  var result;
  var index = 0;
  var item = this[index++];
  item = (item == null) ? "" : ToString(item);
  result = item;
  while(index !== length) { // (1)-2
    item = this[index++];
    item = (item == null) ? "" : ToString(item);
    result += separator + item;
  }
  return result;
}
// argが数値の場合は厳密には違うかも。
// 以後もたびたび登場する予定。
function ToString(arg) {
  return ""+arg.valueOf();
}
  • 0 から数値化された lengthプロパの値までを処理する(1)
  • 戻り値は必ず文字列でありthis.length-1 個のセパレータを必ず含む
  • thisArrayでない場合例えば Array.prototype.join.call({length:10+1}, " ") で長さが 10 のスペースを得ることが期待される

[javascript] ArrayStringの故意に汎用的なメソドたち (2)

 Array.prototype.pop()

function() {
  var length = this.length>>>0;
  if(length === 0) {
    this.length = length; // (1)-1
    return; // undefined
  }
  length -= 1;
  var result = this[length];
  delete this[length];
  this.length = length; // (1)-2
  return result;
}
  • (数値化され) this.length1つデクリメトしたプロパを削除しその値を返す。
  • pop()を呼び出すと副作用で lengthプロパが数値になる(1)

 Array.prototype.push([item1[, item2[, ...]]])

function() {
  var length = this.length>>>0;
  for(var i = 0; i < arguments.length; ++i) {
    this[length++] = arguments[i];
  }
  this.length = length; // (1)
  return length;
}
  • (数値化され) this.lengthをインクリメトしながら引数をプロパに設定していく
  • push()を呼び出すと副作用で lengthプロパが必ず数値になる(1)

[javascript] ArrayStringの故意に汎用的なメソドたち (3)

 Array.prototype.reverse()

function() {
  var length = this.length>>>0;
  var mid = Math.floor(length/2);
  for(var p = 0; p !== mid; ++p) {
    var q = length-p-1;
    var P = this[p], Q = this[q];
    if(!(q in this)) {
      delete[p];
    } else {
      this[p] = Q;
    }
    if(!(p in this)) {
      delete[q];
    } else {
      this[q] = P;
    }
  }
  return this;
}
  • 0 から数値化された lengthプロパの値までを処理する
  • reverse呼び出し前後でプロパ(疎な要素など)が増えたり減ったりはしない

 Array.prototype.sort(comparefn)

sort()についてはthis.length>>>0 が用いられることとthisを変更して thisを返すことだけを書いておいてi, j要素を比較する手順について

function compare(i, j) {
  // (A) 存在しない要素を最後尾へ。
  if(!(i in this)) {
    if(!(j in this)) {
      return +0; // (1)
    } else {
      return 1;
    }
  } else if(!(j in this)) {
    return -1;
  }
  var I = this[i], J = this[j];
  // (B) undefinedな要素を後ろへ。
  if(typeof(I) === "undefined") {
    if(typeof(J) === "undefined") {
      return +0; // (1)
    } else {
      return 1;
    }
  } else if(typeof(J) === "undefined") {
    return -1;
  }

  // (C) ユーザー比較関数
  if(typeof(comparefn) !== "undefined") {
    return comparefn(I, J);
  }
  // (D) デフォルト比較方法 (文字列化して昇順)
  I = ToString(I), J = ToSTring(J);
  if(I < J) {
    return -1;
  } else if(I > J) {
    return 1;
  }
  return +0;
}
  • デフトの比較関数はやっぱり要素を文字列化していた(20080207p01数値配列を期待通りにソトすることはできな)
  • +0 と書いてみたがスクリトで +0-0 を区別することはできない(1)

[javascript] ArrayStringの故意に汎用的なメソドたち (4)

 Array.prototype.shift()

function() {
  var length = this.length>>>0;
  if(length === 0) {
    this.length = length; // (1)
    return; // undefined
  }

  var result = this[0];

  // ひとつずつ前へスライド
  for(var k = 1; k !== length; ++k) {
    if(k in this) {
      this[k-1] = this[k];
    } else {
      delete this[k-1];
    }
  }

  delete this[length-1];
  this.length = length-1;
  return result;
}
  • 0 から数値化された lengthプロパの値までを処理する
  • shift()を呼び出すと副作用で lengthプロパが必ず数値になる(1)

 Array.prototype.unshift([item1[, item2[, ...]]])

function() {
  var length = this.length>>>0;
  // 要素を引数の数だけ後ろへシフト。
  for(var k = length; k !== 0; --k) {
    var j = k-1;
    if(j in this) {
      this[j + arguments.length] = this[j];
    } else {
      delete this[j + arguments.length];
    }
  }
  // 先頭に引数をコピー
  for(var k = 0; k !== arguments.length; ++k) {
    this[k] = arguments[k];
  }

  this.length = length + arguments.length; // (1)
  return this.length;
}
  • 0 から数値化された lengthプロパの値までを処理する
  • unshift()を呼び出すと副作用で lengthプロパが必ず数値になる(1)

[javascript] ArrayStringの故意に汎用的なメソドたち (5)

 Array.prototype.slice(start, end)

function(start, end) {
  var array = [];
  var array_index = 0;
  var length = this.length>>>0;
  start = ToInteger(start);
  start = (start < 0) ? Math.max(length+start, 0) : Math.min(start, length);
  end = (typeof(end) === "undefined") ? length : ToInteger(end);
  end = (end < 0) ? Math.max(length+end, 0) : Math.min(end, length);
  for(var k = start; k < end; ++k) {
    if(k in this) {
      array[array_index++] = this[k];
    } else {
      ++array_index; // (1)
    }
  }
  array.length = array_index;
  return array;
}
function ToInteger(x) {
  x = +x;
  if(isNaN(x)) {
    return +0; // (2)
  }
  if(x === 0 || !isFinite(x)) {
    return x;
  }
  // 0 に近づける。(小数点以下を切り捨てる)
  return (0 <= x) ? Math.floor(x) : Math.ceil(x);
}
  • 数値化された this.lengthと引数 start, end を元に範囲を決定し処理する
  • startendには負数も使用可
  • thisの存在しない要素がコピーされることはないが無視されるわけではない(1)
  • +0 と書いたがスクリトが +0-0 を区別することはできない(2)

 Array.prototype.splice(start, deleteCount[, item1[, item2[, ...]]])

function(start, deleteCount) {
  var array = [];
  var length = this.length>>>0;
  start = ToInteger(start);
  start = (start < 0) ? Math.max(length+start, 0) : Math.min(start, le  deleteCount = ToInteger(deleteCount);
  deleteCount = Math.min(Math.max(deleteCount, 0), length-start);
  // 取り出す要素をコピー。(まだ削除はしない)
  for(var k = 0; k !== deleteCount; ++k) {
    var l = start+k;
    if(l in this) {
      array[k] = this[l];
    }
  }
  // (A) 要素を後ろへずらして空きを作る
  //     (後で上書きされてしまう要素までずらしてない?)
  if(deleteCount < arguments.length) {
    for(var k = length-deleteCount; k !== start; --k) {
      var l = k + deleteCount -1;
      var m = k + arguments.length -1;
      if(l in this) {
        this[m] = this[l];
      } else {
        delete this[m];
      }
    }
  // (B) 要素を前へ詰めて、空きを新要素と同じ数にする
  } else if(arguments.length < deleteCount) {
    for(var k = start; k !== length-deleteCount; ++k) {
      var l = k + arguments.length;
      var m = k + deleteCount;
      if(m in this) {
        this[l] = this[m];
      } else {
        delete this[l];
      }
    }
    // (なぜ逆順で削除する?)
    for(var k = length; k !== length-deleteCount+arguments.length; --k) {
      delete this[k-1];
    }
  }
  // 追加する要素で thisを上書き
  if(arguments.length === deleteCount) {
    for(var k = 0; k !== arguments.length; ++k) {
      this[start+k] = arguments[k];
    }
  }
  this.length = length - deleteCount + arguments.length;
  return array;
}
  • 数値化された this.lengthと引数 start を元に範囲を決定し処理する
  • startには負数も使用可
  • splice()を呼び出すと副作用で lengthプロパが必ず数値になる
  • splice()は仕事を抱え込みすぎ

[javascript] ArrayStringの故意に汎用的なメソドたち (6)

パターンがわかってきた

 Arrayのビトインメソドは concat()を除いてthis.length32-bit unsigned int(UINT32)に変換したものを処理範囲の上限として利用する(下限はもちろん 0)

  • concat()thisや引数が Arrayかどうかで処理を変えるから(利用するとき) this.lengthを直接用いる

 自身の長さを変更するメソ(pop(), push(), shift(), unshift(), splice())を呼び出すと lengthプロパは必ず UINT32に変更される

  • 自身の長さを変更しない reverse(), sort()自身を変更しない concat(), join(), slice()を呼び出しても lengthは変化しない

 あんまり使えない

 Arrayの多くのメソドは自己破壊的でStringに適用することができない

Firefox(JavaScript1.5?)では適用すると Array.prototype.method.call(string) is read-onlyという警告がいくつも出る

一度の呼び出しでいくつも出ることから read-onlyなのはメソドではなく自己破壊的な Array.prototype.method()を適用された stringだと思われる

警告が出るだけで戻り値は得られるので pop()で末尾の文字shift()で先頭の文字が得られる

unshift()push()の返す数値は使えない(その長さを持った文字列は存在しないの)

reverse(), sort()は全く役に立たない役に立つ値を返さないし並べ替えには失敗しているから

slice()String.prototypeに同名のメソドがすでに存在する

splice()がちっと面白くsplice(startdeleteCount) の引数はちょうど String.prototype.substr(start, length)と対応するがってくるのが substr()->"文字" なのに対しsplice()->["","","列"] となる

string.split("")の代わりに Array.prototype.splice.call(string, 0)としてみるのはいかが? (警告が出る上に IE7では使えません)

 そもそも IEJScript5.7では文字列に添え字を使ってアクセスできないので Arrayのメソドを適用しても意味がない

Stringではなく配列のようなオブジ(コレクションとか argumentsとか)に適用するのが正解

 concat()join()は広く使える

 配列化1 (びみ)
// unknown_objectが配列なら unknown_objectのコピー、
// それ以外なら unknown_objectを唯一の要素とする配列が返る。
var array = Array.prototype.concat.call(unknown_object);
 配列化2 (IEでは使えな)
// lengthプロパティと [] での要素アクセスが可能な、
// 配列っぽいオブジェクト(NodeListとか argumentsとか)を配列化。
var array = Array.prototype.slice.call(document.getElementsByTagName("pre"), 0);
 繰り返し文字列 (っこう使え)
// "        " が得られます。
var softtab = Array.prototype.join.call({length:8+1}, " ");
// softtabを使ったレベル3のインデントが得られます。
var indent = new Array(3+1).join(softtab); // prototypeいらねー

二番目の書き方を使うなら Arrayのビトインメソドを他のオブジトに使い回すという趣旨が……

[javascript] ArrayStringの故意に汎用的なメソドたち (7)

今回から String.prototypeのメソStringは自己破壊的なメソドを持っていないからArrayのビトインメソドと違って適用範囲が広いことを期待している

……と思ったがStringのビトインメソドはどれも事前に ToString(this)を呼んでしまう(ToString()については (1))

indexOfで配列の探索ができるんじゃないかと期待していたが無理だったっかり

これにて終了

split()に渡す正規表現のサブマッチに特別な意味があるとかsplit()はグローバルフラグを無視するとかグローバルフラグの立った正規表現で exec(string)を呼んで空文字列にマッチしたときはこちらで lastIndexプロパ1増やさないといけないとかもやもやしてたことが Under Translation of ECMA-262 3rd Editionにはいろいろ書いてありました訳者に感謝


20080207()

[javascript] 普通に使ってて驚いた JavaScriptのヘンなところ

switch-caseのラベル部分に何でも書ける*という変態的な部分は置いておいて1/20.5になるのと同じ類の話

 [1, 5, 10].sort()

返るのは [1, 10, 5]どうも文字列としてソトされている数字だけの配列だ(と自分が知っている)からって無駄な期待をしてはいけない

 for(var i in [1, 5, 10]) { alert(typeof i) }

stringと表示されるi に入るのが配列の要素 1, 5, 10 ではなく添え字(key)だというだけで意外だがさらに意表をついてそれが数字でもなく"0", "1", "2" i+1 としても隣の要素にはアクセスできないarray[1] = 5; と数字の添え字で代入した後でも同じだった

for-inープは遅いと評判だったので使っておらず最近まで知らなかった

ついでにいうと for-inとは別の in 演算子の存在もつい最近まで知らなかった(SHJSのソースを読んで発見し)

グローバル変数の存在確認方法も SHJSを読んで知った今までは typeof(global_var) == "undefined" とやっていたこれは存在確認とは微妙に意味が違う

deleteできるグローバル変数とできないグローバル変数については amachangのブログを読んで知った

 オブジトのプロパ連想配列のキ配列の添え字ぜんぶ同じぜんぶ文字列

  • プロパ名はすべて文字列化されてからオブジトに渡されること
  • 配列は特殊な lengthプロパを持った Object

ということを知っていれば for-inープで文字列が渡されても驚くことはない(ゃあなぜ驚い)

var object = {};
alert(""+ (1==="1")); // false
o[1] = 1;
o["1"] = "1";
alert(""+ (o[1]===o["1"])); // true

var a = [1,2,3];
var b = [1,2,3];
alert(""+ (a===b)); // false
o[a] = a;
o[b] = b;
alert(""+ (o[a]===o[b])); // true

var s = "1,2,3";
alert(""+ (a===s)); // false
o[s] = s;
alert(""+ (o[a]===o[s])); // true

* http://arton.no-ip.info/diary/20061224.html#p02


20080206() パスワドを間違えたときに Caps Lockがオンと教えてくれる Vista

[javascript] 継承prototypeへの代入

こんなやりとりがあった

  1. 404 Blog Not Found:javascript - 勝手に添削 - JavaScript入門
  2. 「勝手に添削 - JavaScript 入門を勝手に添削 - IT戦記

dankogaiの指摘の一つMyObject.prototype = { /* ... */ }は避けるべしについて

MyBox.prototype = Box.prototype; // ここで継承しているのに
MyBox.prototype = {              // ここで台無し
  speed:8
};

指摘には同意するが上の例だと親の Boxにも MyBoxと共通のクラス変数 speedが追加されている

amachangの反論にあるように、継承するなら次の書き方の方が優れている

MyBox.prototype = new Box; // new Box()と同じ

ところでamachangはこう続けているこれがプトタイプ継承の正しい形式であり

MyBox.prototype = {};
MyBox.prototype = new Object;

指摘を受けた上段の書き方は下段のシンタックスシュガーなので両方とも Objectからの正しい継承方法であるこのことがわかっていれば他から継承しておきながら Objectからも継承して台無しにするような間違いは犯さない(だから必要に応じて違う書き方をすれば良いし必要がなければ .prototype={}と書いても良い)

でも違う理由で MyObject.prototype = {}; という書き方は避けている 理由は一つで

MyObject = function(){};
MyObject.prototype = {/* MyObjectの定義 */};
alert(new MyObject().constructor); // function Object(){ [natiive code] } と表示

constructorプロパMyObjectではなくなってしまっているこれは MyObject.prototype = new MyParent; としたときも同じで constructorMyParentになる

constructorプロパだけを気にするなら

MyObject.prototype = { constructor:MyObject.prototype.constructor };

というように明示的にコピーすればごまかせるが一時しのぎ感がぷんぷんしている(constructor以外のプロパが追加されたらその都度書き換えるの)

だから、継承の仕方で逆につっこまれてしまったがdankogaiが提示した 3つの方法に自分は同意していてそのうちの一つ目を自分は使っている


20080204()

[SHJS][javascript] sh_main.jsの高速化

SHJSのブラウザでの実行時間を削るには sh_main.js(SHJSのメイスクリ)を速くするか正規表現を効率的なものにする方法がある(>遅い正規表現(20080116p01))

正規表現に関してできることは限られるうえ知識も少ない(『詳説 正規表現 第三版待ち)ので可能な限り文字クラスや文字集合といわれるものを使うように気を付けただけにとどまる(sh_ruby.js, sh_javascript.js)

メイスクリトの sh_main.jsに対してできることは多いこの日記の現在?の最新ページ(2008112日から7)を表示してsh_highlightDocument()前後での経過時間を表示したところこのようになった

Firefox2IE7(64-bit)IE7(32-bit)Opera9.25
sh_main.js (0.4.2)935ms1050ms1270ms1260±150ms
改変版600ms680ms865ms1200±150ms
削減率36%35%32%5%

ハイラト対象が少なくて数ミリ秒で処理が終わるような場合はオーバーヘドのために改変版の方が 1-2ミリ秒遅くなるがそれよりもスクリトがブラウザをロックする時間が長くなるような場合にこそ速度改善が必要なので OK

代償としてファイルサイズが sh_main.js10.5KiBから 12.7KiB+2.2KiBjsmin圧縮後の sh_main.min.js6.22KiBから 7.82KiB+1.60KiBApacheによる gzip圧縮やブラウザのキッシュに期待します。

 () Operaについて

普段は全く Operaを使わないし詳しくもないむしろ Operaではキーボドを使ったブラウジングもままならないそんな人間が Firefox+Firebugを頼りに sh_main.jsの修正を行ったので Operaの速度が改善しないのは仕方のない部分がある(IEは改善したが)(あんだけいじータルで変わらない方がすごいどこが足を引っぱっているのだろう)リテラル文字列と Stringオブジトの差が他のブラウザより大きいらしいがそれが原因?

EfficientJavaScript - Dev.Opera - 効率的な JavaScript (www.hyuki.com)

Operaでの JavaScriptの実行時間が他のブラウザに比べて長いのははっきりした理由があってOperaはスクリトが全力疾走中であってもユーザーの操作に対する反応を後回しにしたりしないこれは偉いーザーを待たせない代わりにスクリトが遅れるのは当然の代償で仕方がない

スクリトでなく再描画が律速してるから改善しないということ?

 (かんせいしたへびのえにあしをかきくわえるこういとまでは言わない) この日記で使用中のファイルへのリンク

(常に最新版だが一時的にバグが混入していることがあるか)

 追記@日:sh_main.jsをちっと修正

すぐ上のリンク先はすでに変更が反映されている

これら二つの記事を参考に escapeHTML()を変更した測定に使ったページでは 9000回ちかく呼び出されるメソドなので影響はバカにならないといっても 600msったのが 590msを切るようになったというレベルむしろ下請けfunctionを隠蔽できたことの方が嬉しい

escapeHTML()自体sh_builderのインターフェイスではないので外部から呼び出せないようにすべきかもしれないがfunctionをかぶせるたびに呼び出しのオーバーヘドが増える気がしてそうはしていない

 追記@今日

SHJSpatternStackオブジトは外部と完全に独立して動作するのにsh_highlightString()が呼ばれるたびに無名クラスとそのイスタスを作成するような方法がとられているトラクタと prototypeを書こう(sh_highlightString()HTML文書内の <pre class="sh_XXX">の数だけしか呼ばれないから影響は小さい件のページでは 58)

sh_highlightString()からしか使われないのにスタックの可視範囲が広がるのが気になるならっき覚えた無名functionで二つをくるんでしまえば良い

var sh_highlightString = (function(){
  var Stack = function(){
    this.stack_ = [];
  };
  Stack.prototype.getLength = function(){/* ... */};
  // ……
  return function(){
    var patternStack = new Stack();
    /* sh_highlightStringの中身がつづく…… */
  };
})();

まあ速度が改善するわけではないので書き直さないんだけど

 追記@2008-02-25

innerHTMLtextContentinnerTextの使用は堕落だという気もするが冗長な上に呼び出しを重ねることで遅くなる DOMメソドがいけない


20080116() PythonかわいいよPython

[SHJS][SakuraEditor][javascript] SHJSSakuraEditor用のハイラトルールファイル

SHJSjavascript定義ファイル(lang/sh_javascript.js)の元になったファイル(javascript.lang)の中身がこれ

include "java.lang"

subst keyword = "abstract|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|final|finally|for|function|goto|if|implements|in|instanceof|interface|native|new|null|private|protected|prototype|public|return|static|super|switch|synchronized|throw|throws|this|transient|true|try|typeof|var|volatile|while|with"

javaて……ーワドにしても使ったことのないものがいっぱい

あまりにあんまりなんで一から書いた(sh_javascript.js, sh_javascript.min.js) 参照したのは JScript5.5HTMLHelpなので JScript.NETECMAScript4には対応していない古典的な JavaScript

ついでに同じものを SakuraEditorにも(javascript_keywords.zip)


20080115()

[javascript] ラベル付きブロックと break - JScript

オブジトリテラルの存在も JScript5.5HTMLHelpでの扱いが一行しかなかった*ために気付くのが遅れたがこれはその上をいく古典的な javascriptで何年ぶりの新発見しかもこれもちゃんと書いてあった(但し一行)

block:
{
  break block;
}

ープにラベルを付けてトしたループから外側のループを continueしたり breakしたりできるのは知っていた(が忘れていた)ラベル付きブロックと breakのこの使い方は全く知らなかった

labelのスコープはローカル変数と同じみたいでfunctionをまたいで goto代わりには使えなかった

 余談

 function(){ alert(1) }();
 void function(){ alert(2) }();
 (function(){ alert(3) })();

alertが表示されるのは 23二行目ので初めて voidの使い途を見つけたと思ったのだったが三番目の書き方もあるらしくまたそれが一般的っぽいまあなんとなくわかるあれでし

 余談の追記@2008-02-25

1行目の functionだけ functionExpressionではなく functionDeclarationだと解釈されて続く () は関数呼び出しのかっこではなく式をグルーピングするかっこになるのだと(functionDeclarationでは関数名を省略できないのだから(省略できる)functionExpressionだと解釈してくれても良さそうなものだ)

そういう理由なのでこんなのもあり

+function(){ alert(4) }();
(function(){ alert(5) }());

* JScript>ーザーズ ガ>JScriptの基本>JScriptドの記述

 JScript>ランゲージ リファレ>ステトメ>break ステトメ

 ローカル変数なら自分より外側のものが見えるラベルは見えないのでそれより厳しい


20080105()

[SHJS][javascript]SHJS Ruby定義ファイル(sh_ruby.js) 私的改訂版 まとめ(2008-01-17 最終更)

ァイルはこちら20080101p01

あいだにコメトを挟みながら一連の DIFF差分(sh_ruby.js.diff)を見ていく

 っこ(?:)を省略して減量

--- sh_ruby.js.original	Fri Aug  3 12:16:32 2007
+++ sh_ruby.js	Sat Jan 19 01:35:32 2008
@@ -2,123 +2,427 @@
   this.sh_languages = {};
 }
 sh_languages['ruby'] = [
   [
     {
-      'regex': /\b(?:require)\b/g,
-      'style': 'sh_preproc'
+      regex: /\brequire\b/g,
+      style: 'sh_preproc'
     },

 #から後ろはすべてコメトなんていう強力すぎるルールは後ろに移動して優先度を下げる

-    {
-      'next': 1,
-      'regex': /#/g,
-      'style': 'sh_comment'

 必ず最初に実行される処理(BEGIN)最後に実行される処理(ENDat_exit)スクリトを終了する(exit)など無視できない働きを持ったメソドをハイラ

+    { // part of Kernel methods.
+      regex: /\b(?:exit!?|(?:abort|at_exit|BEGIN|callcc|END|eval|exec|fork|load|spawn|syscall|system|trap|warn)\b)/g,
+      style: 'sh_preproc'
     },

 \bの後ろの +-は絶対にマッチしないので前に出してマッチするように

 数値リテラルの定義が cpp,java,pascal,perl,php,prolog,python,sh,sql,tclと共通だったので Ruby専用に(先頭末尾や連続するアンダースコアやが許容されているがべつに Rubyインタープリタじゃないので気にしな)

     {
-      'regex': /\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,
-      'style': 'sh_number'
+      regex: /[+-]?\b(?:0(?:x[A-Fa-f0-9_]+|d[\d_]+|b[01_]+|o?[0-7_]+)|(?:0\.)?[\d_]+(?:[Ee][+-]?[\d_]+)?)\b/g,
+      style: 'sh_number'
     },

 javascriptのオブジトリテラルのプロパ名部分は引用符を省ける(ので省く)

     {
-      'next': 2,
-      'regex': /"/g,
-      'style': 'sh_string'
+      next: 2,
+      regex: /"/g,
+      style: 'sh_string'
     },
     {
-      'next': 3,
-      'regex': /'/g,
-      'style': 'sh_string'
+      next: 3,
+      regex: /'/g,
+      style: 'sh_string'
     },

 <<メソドや <<HEREDOCUMENTに誤ってマッチしないようにHTMLタグっぽいものだけをハイラ(そもそもなんで <hoge>を文字列としてハイラトするのかは GNU Source-highlightのみぞ知る)

     {
-      'next': 4,
-      'regex': /</g,
-      'style': 'sh_string'
+      next: 4,
+      regex: /<(?=[\w\/])/g,
+      style: 'sh_string'
     },

 /regexp/i 型の正規表現リテラルの条件を厳しくして URLに誤ってマッチしないようにまたオプション部分もマッチに含めるように

     {
-      'regex': /\/[^\n]*\//g,
-      'style': 'sh_regexp'
+      regex: /\/(?:\\.|[^\n\\\/])*\/[eimnosux]*(?![A-Za-z])/g,
+      style: 'sh_regexp'
     },

 別途定義したので不完全な %r{regexp}の定義は削除

     {
-      'regex': /(%r)(\{(?:\\\}|#\{[A-Za-z0-9]+\}|[^}])*\})/g,
-      'style': ['sh_symbol', 'sh_regexp']

 定義と処理の流れに関わるキーワドやメソドを選択除外したものはすべて定数や無視できないメソドとして別に定義した

+      regex: /(?:\b(?:alias|attr(?:_reader|_writer|_accessor)?|begin|break|case|do|else|elsif|end|ensure|for|if|in|include|lambda|loop|next|proc|raise|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield|and|not|or|def|class|module|catch|fail|throw)\b|&&|\|\|)/g,
+      style: 'sh_keyword'
     },
     {
-      'regex': /\b(?:alias|begin|BEGIN|break|case|defined|do|else|elsif|end|END|ensure|for|if|in|include|loop|next|raise|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield|false|nil|self|true|__FILE__|__LINE__|and|not|or|def|class|module|catch|fail|load|throw)\b/g,
-      'style': 'sh_keyword'

 不要なグループ化(?:)とエスケープ(\=)を削除

+      next: 5,
+      regex: /^=begin/g,
+      style: 'sh_comment'
     },
     {
-      'next': 5,
-      'regex': /(?:^\=begin)/g,
-      'style': 'sh_comment'

 グローバル変数は別に定義したのでここではイスタス変数とクラス変数のみを扱うことに

+      regex: /@@?[A-Za-z_][A-Za-z0-9_]*/g,
+      style: 'sh_type'
     },
-    {
-      'regex': /(?:\$[#]?|@@|@)(?:[A-Za-z0-9_]+|'|\"|\/)/g,
-      'style': 'sh_type'

 グローバル変数専用のルールを追加

+    { // global variables
+      regex: /\$(?:[_&~`'\+\?!@=\/\\,;\.<>\*\$:"]|-?[A-Za-z0-9_]+\b)/g,
+      style: 'sh_type'
+    },

 各種かっこ(<[{}]>)を使った正規表現コマ文字列配列文字列シンボルリテラルのルールを追加(後ろの方に関連する追加部分あ)

+    { // %r(regexp)
+      next: 6,
+      regex: /%r(?=[\(<\[\{])/g,
+      style: 'sh_regexp'
+    },
+    { // %x(command), %w(array)
+      next: 11,
+      regex: /%[xWw](?=[\(<\[\{])/g,
+      style: 'sh_normal'
+    },
+    { // %(string), %s(symbol)
+      next: 16,
+      regex: /%[Qqs]?(?=[\(<\[\{])/g,
+      style: 'sh_string'
+    },

 っこ以外の記号を使った正規表現コマ文字列配列文字列シンボルリテラルのルールを追加

+    { // %r!regexp!i
+      regex: /%r([ -'*-\/:;=?@\\^_`|~])(?:\\.|.)*?\1[eimnosux](?![A-Za-z])/g,
+      style: 'sh_regexp'
+    },
+    { // %x!command!, %w!array!
+      regex: /%[xWw]?([ -'*-\/:;=?@\\^_`|~])(?:\\.|.)*?\1/g,
+      style: 'sh_string'
+    },
+    { // %!string!, %s!symbol!
+      regex: /%[Qqs]?([ -'*-\/:;=?@\\^_`|~])(?:\\.|.)*?\1/g,
+      style: 'sh_string'
+    },

 シンボルリテラルのルールを追加(コロンにクーテーションマークが続くタイプのシンボル(:"symbol")は現状でもコロンがsh_symbolその後ろがsh_stringとして認識されるので対応する必要がな)

+    { // Symbol
+      regex: /(:)((?:@@?|\$|[A-Za-z_])\w+\b[!\?]?)/g,
+      style: ['sh_symbol', 'sh_string']
+    },
+    { // Symbol
+      regex: /(:)(\+|~|\*\*?|-|\/|%|<=>|<<?|>>?|^|===?|=~|!~|&|\|)(?=[^\w\d]|$)/g,
+      style: ['sh_symbol', 'sh_string']
+    },

 定数のルールを追加

+    { // Constants
+      regex: /\b[A-Z]\w+\b/g,
+      style: 'sh_function'
+    },
+    { // Constants
+      regex: /\b(?:self|nil(?!\?)|true|false|__FILE__|__LINE__)\b/g,
+      style: 'sh_function'
+    },

 大文字や数字で始まって ?!で終わるメソドはないので(ないよね?)そのように正規表現を変更

+    { // don't highlight ? and ! as symbols if they are part of a method call
+      regex: /\b[a-z_]\w*[!\?]/g,
+      style: 'sh_normal'
     },
     {
-      'regex': /[A-Za-z0-9]+(?:\?|!)/g,
-      'style': 'sh_normal'

 ::hogeSymbolと誤認しないように先に ::sh_symbolにしてしまう

 プロパ名部分の引用符を省略

+      regex: /~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|::?|;|,|\.|\/|\?|&|<|>|\|/g,
+      style: 'sh_symbol'
     },
     {
-      'regex': /~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g,
-      'style': 'sh_symbol'
+      regex: /(#)(\{)/g,
+      style: ['sh_symbol', 'sh_cbracket']
     },
     {
-      'regex': /(#)(\{)/g,
-      'style': ['sh_symbol', 'sh_cbracket']
+      regex: /\{|\}/g,
+      style: 'sh_cbracket'
     },
     {
-      'regex': /\{|\}/g,
-      'style': 'sh_cbracket'

 #一行コメト ルールを前の方から最後のここまで移動

+      next: 1,
+      regex: /#/g,
+      style: 'sh_comment'
     }
   ],

 プロパ名部分の引用符を省略

 グループ化(?:)の代わりに文字クラス( [] )を使用して字数削減

 'exit'するときの 'style'指定はなくてもかまわないので削除

 不要なグループ化とエスケープを削除(?:\=end)

   [
     {
-      'exit': true,
-      'regex': /$/g
+      exit: true,
+      regex: /$/g
     }
   ],
   [
     {
-      'exit': true,
-      'regex': /$/g
+      exit: true,
+      regex: /$/g
     },
     {
-      'regex': /\\(?:\\|")/g
+      regex: /\\[\\"]/g
     },
     {
-      'exit': true,
-      'regex': /"/g,
-      'style': 'sh_string'
+      exit: true,
+      regex: /"/g
     }
   ],
   [
     {
-      'exit': true,
-      'regex': /$/g
+      exit: true,
+      regex: /$/g
     },
     {
-      'regex': /\\(?:\\|')/g
+      regex: /\\[\\']/g
     },
     {
-      'exit': true,
-      'regex': /'/g,
-      'style': 'sh_string'
+      exit: true,
+      regex: /'/g
     }
   ],
   [
     {
-      'exit': true,
-      'regex': /$/g
+      exit: true,
+      regex: /$/g
     },
     {
-      'exit': true,
-      'regex': />/g,
-      'style': 'sh_string'
+      exit: true,
+      regex: />/g
     }
   ],
   [
     {
-      'exit': true,
-      'regex': /^(?:\=end)/g,
-      'style': 'sh_comment'
+      exit: true,
+      regex: /^=end/g
     }
+  ],

 各種かっこ(<[{}]>)を使ったリテラルのルール(後半部)

+  [ // state 6-10: %r(regexp)
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 7,
+      regex: /\(/g,
+      style: 'sh_regexp'
+    },
+    {
+      next: 8,
+      regex: /</g,
+      style: 'sh_regexp'
+    },
+    {
+      next: 9,
+      regex: /\[/g,
+      style: 'sh_regexp'
+    },
+    {
+      next: 10,
+      regex: /\{/g,
+      style: 'sh_regexp'
+    },
+    {
+      exit: true,
+      regex: /[)>\]}][eimnosux]*/g,
+      style: 'sh_regexp'
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 6,
+      regex: /(?=\()/g
+    },
+    {
+      exit: true,
+      regex: /(?=\))/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 6,
+      regex: /(?=<)/g
+    },
+    {
+      exit: true,
+      regex: /(?=>)/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 6,
+      regex: /(?=\[)/g
+    },
+    {
+      exit: true,
+      regex: /(?=])/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 6,
+      regex: /(?={)/g
+    },
+    {
+      exit: true,
+      regex: /(?=})/g
+    }
+  ],
+  [ // state 11-15: %x(command)
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 12,
+      regex: /\(/g,
+      style: 'sh_normal'
+    },
+    {
+      next: 13,
+      regex: /</g,
+      style: 'sh_normal'
+    },
+    {
+      next: 14,
+      regex: /\[/g,
+      style: 'sh_normal'
+    },
+    {
+      next: 15,
+      regex: /\{/g,
+      style: 'sh_normal'
+    },
+    {
+      exit: true,
+      regex: /[)>\]}]/g,
+      style: 'sh_normal'
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 11,
+      regex: /(?=\()/g
+    },
+    {
+      exit: true,
+      regex: /(?=\))/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 11,
+      regex: /(?=<)/g
+    },
+    {
+      exit: true,
+      regex: /(?=>)/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 11,
+      regex: /(?=\[)/g
+    },
+    {
+      exit: true,
+      regex: /(?=])/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 11,
+      regex: /(?={)/g
+    },
+    {
+      exit: true,
+      regex: /(?=})/g
+    }
+  ],
+  [ // state 16-20: %Q(string)
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 17,
+      regex: /\(/g,
+      style: 'sh_string'
+    },
+    {
+      next: 18,
+      regex: /</g,
+      style: 'sh_string'
+    },
+    {
+      next: 19,
+      regex: /\[/g,
+      style: 'sh_string'
+    },
+    {
+      next: 20,
+      regex: /\{/g,
+      style: 'sh_string'
+    },
+    {
+      exit: true,
+      regex: /[)>\]}]/g,
+      style: 'sh_string'
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 16,
+      regex: /(?=\()/g
+    },
+    {
+      exit: true,
+      regex: /(?=\))/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 16,
+      regex: /(?=<)/g
+    },
+    {
+      exit: true,
+      regex: /(?=>)/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 16,
+      regex: /(?=\[)/g
+    },
+    {
+      exit: true,
+      regex: /(?=])/g
+    }
+  ],
+  [
+    {
+      exit: true,
+      regex: /$/g
+    },
+    {
+      next: 16,
+      regex: /(?={)/g
+    },
+    {
+      exit: true,
+      regex: /(?=})/g
+    }
   ]
 ];