20

Closure Compilerに次のようなものを与えると:

window.array = '0123456789'.split('');

これを「コンパイル」します。

window.array="0,1,2,3,4,5,6,7,8,9".split(",");

お分かりのように、今はもっと大きいです。Closure Compilerがこれを行う理由はありますか?

4

3 に答える 3

21

これが起こっていることだと思いますが、私は決して確実ではありません...

コンマの挿入を引き起こすコードはtryMinimizeStringArrayLiteralPeepholeSubstituteAlternateSyntax.javaあります。

このメソッドには、ハフマン符号化が低い可能性が高い文字のリストが含まれているため、他の文字よりも分割することが望ましいです。次のようなことを試してみると、この結果を確認できます。

"a b c d e f g".split(" "); //Uncompiled, split on spaces
"a,b,c,d,e,f,g".split(","); //Compiled, split on commas (same size)

コンパイラは、分割しようとしている文字を、有利と思われる文字に置き換えます。これは、文字列の文字を繰り返し処理し、文字列内で発生しない最も有利な分割文字を見つけることによって行われます。

// These delimiters are chars that appears a lot in the program therefore
// probably have a small Huffman encoding.
NEXT_DELIMITER: for (char delimiter : new char[]{',', ' ', ';', '{', '}'}) {
  for (String cur : strings) {
    if (cur.indexOf(delimiter) != -1) {
      continue NEXT_DELIMITER;
    }
  }
  String template = Joiner.on(delimiter).join(strings);
  //...
}

上記のスニペットでは、コンパイラが分割に最適であると主張する文字の配列を確認できます。コンマが最初です (上記のスペースの例では、スペースがコンマに置き換えられているのはそのためです)。

分割する文字列が空の文字列である場合にコンマを挿入するのは、単に見落としである可能性があると思います。この場合、特別な処理はないように見えるため、他のsplit呼び出しと同様に処理され、各文字は上記のスニペットに示されている配列の最初の適切な文字と結合されます。


コンパイラがメソッドを処理する方法の別の例split:

"a,;b;c;d;e;f;g".split(";"); //Uncompiled, split on semi-colons
"a, b c d e f g".split(" "); //Compiled, split on spaces

今回は、元の文字列に既にカンマが含まれているため (カンマ文字で分割したくないため)、低ハフマン エンコード文字の配列からカンマを選択することはできません。そのため、次の最適な選択は(スペース) を選択します。


アップデート

これについてさらに調査した結果、これは間違いなくバグではありません。この動作は実際には設計によるものであり、Closure コンパイラがサイズよりもコンパイルされたコードの速度を優先する傾向があることを念頭に置いて考えると、これは非常に賢い小さな最適化だと思います。

上記で、ハフマン符号化について数回言及しました。非常に簡単に説明すると、ハフマン コーディング アルゴリズムは、エンコードされるテキストに現れる各文字に重みを割り当てます。ウェイトは、各キャラクターの出現頻度に基づいています。これらの頻度は、最も一般的な文字をルートとする二分木を構築するために使用されます。つまり、最も一般的な文字はツリーのルートに近いため、デコードが速くなります。

また、ハフマン アルゴリズムは gzip で使用される DEFLATE アルゴリズムの大部分を占めているためです。したがって、Web サーバーが gzip を使用するように構成されている場合、ユーザーはこの巧妙な最適化の恩恵を受けることができます。

于 2012-04-18T14:37:33.840 に答える
5

この問題は 2012 年 4 月 20 日に修正されました。リビジョンを参照して ください。

于 2012-04-24T15:49:45.227 に答える
4

皮肉なことにsplit、コンパイルされたコードでは、ソースとは何の関係もありませんsplit。検討:

Source  : a = ["0","1","2","3","4","5"]
Compiled: a="0,1,2,3,4,5".split(",")

ここでsplitは、長い配列を表す方法にすぎません(すべての引用符とコンマの合計がsplit(",""))より長くなるのに十分な長さです。それで、あなたの例では何が起こっているのですか?まず、コンパイラは定数に適用された文字列関数を確認し、すぐに評価します。

'0123456789'.split('') => ["0","1","2","3","4","5","6","7","8","9"]

後のある時点で、出力を生成するときに、コンパイラはこの配列を「長い」と見なし、上記の「分割」形式で書き込みます。

["0","1","2","3","4","5","6","7","8","9"] => "0,1,2,3,4,5,6,7,8,9".split(",")

ソース内のすべての情報split('')は、この時点ですでに失われていることに注意してください。

ソース文字列が短い場合、余分な分割なしで配列配列形式で生成されます。

Source  : a = '0123'.split('')
Compiled: a=["0","1","2","3"]
于 2012-04-18T14:59:48.847 に答える