0

注: 再帰的に間違った単語を使用している可能性があります

部分文字列の繰り返しを検索して、長いテキスト文字列を実行するスクリプトを作成しようとしています。このために、基本的に 12 から 3 までの for ループを実行し (繰り返しをチェックしたい文字列の長さ)、それらの長さごとに別の for ループを使用して、その長さの文字列が複数回存在するかどうかを定義します。馬鹿げた例 (テストされていない、デモンストレーションのためだけ):

for(var len=12; len>3; len--) {
    var recurring = [];
    for(var pos = 0; pos < (inputString.length - len); pos++) {
        var mystring = inputString.substr(pos, len);
        // do a regex to check if mystring occurs more than once
        // if so, push it into recurring and remove from inputString to prevent partial duplicates when mystring gets shorter
    }
    // output recurring strings
 }

これはすべて機能しますが、明らかに非常に長いinputStringでこれを実行すると、非常に遅くなります。実際、ブラウザがフリーズし、スクリプトの実行中に表示したい進行状況の出力が一時停止され、スクリプトが完了するとすべてが一度に表示されます。

これを防ぐために、一度に小さなバッチで for ループを実行する関数を作成し、コールバック関数を使用して次のバッチに進みました。これの基本的なバージョンを使用することで、何千ものループを経ていたとしても、以前は for ループであったプロセスの進行状況をページに出力することができました。

var fragmentedFor = function($aFragmentLength, $aTotal, $aFunction, $aCallback) 
{
    var $lStart = 0, 
        $lFragmentLength = $aFragmentLength,
        $lTotal = $aTotal,
        $lFunction = $aFunction,
        $lCallback = $aCallback;

    // simulate a for loop, but in fragments to prevent the browser from freezing
    (function forLoop ($aStart)
    {
        // run x loops at a time
        var $lEnd = $lStart + $lFragmentLength;

        // can't run the loop past the total length
        if($lEnd > $lTotal ) 
            $lEnd = $lTotal;

        // the fragmented for loop
        for(var $i = $lStart; $i < $lEnd; $i++) {
            // run the function here    
            $lFunction({count: $i});
            }

            // next time, start from where we just left
        $lStart += $lFragmentLength;

        // is there room for more loops?
        if($lStart < $aTotal) {
            setTimeout(forLoop, 10);
        } else { 
            // if not, run the callback
            $lCallback();
        }
    })();
}

これを一度だけ実行すると、私が望むことをしているようです:

function myLoop(x) {
    new fragmentedFor(
        10,  // amount of loops per run
        x,  // total amount of loops
        function($aData) { console.log($aData.count); },  
        function() { console.log('finished') }
    );
}
myLoop(1000);

ただし、このようなループの別のインスタンス内にネストされたものを呼び出したい場合、問題が発生します。

new fragmentedFor(
    1,
    5,
    function($aData) { myLoop($aData.count * 100) },
    function() { console.log('finished all') }
)

(現在のファイルをhttp://files.litso.com/vigenere/so/にコピーしました。これには fragmentedFor 関数が含まれているため、基本的にこれら 2 つの関数をコンソールに貼り付けて結果を確認できます。これははるかに簡単でした。気にしなければ、それをjsfiddleに置いてそれを機能させるよりも)

myLoop の最初の実行が開始されたように見えますが、0 * 100 回実行しようとしているため、安全に終了し、2 回目の実行に進みます。myLoop の 2 回目の実行では 100 まで上がるはずですが、突然 3 回の myLoop の実行が始まる前に 19 まで実行されます。

そこから、出力が完全に混乱しているように見えます。これは、コールバックが正しく実装されておらず、ループがお互いの終了を実際に待っていないためだと思います。

ここで何が間違っていますか?問題のある場所でデバッグを開始するにはどうすればよいでしょうか。また、for ループの独立した実行が実際に相互の終了を待機していることを確認するにはどうすればよいでしょうか?

-編集-

古い作業コピーの jsfiddle は次のとおりです: http://jsfiddle.net/u2aKX/ これには fragmentedFor ループが組み込まれていませんが、通常のネストされた for ループと比較してパフォーマンスが 100% 向上するように見えるコールバック関数がいくつかあります。

4

1 に答える 1

1

コールバックが正しく実装されておらず、ループが互いに終了するのを実際に待っていないためだと思います。

ここで何が間違っていますか?

が同期していることをfragmentedFor期待し、$aFunction戻るとすぐに次のループ ターンをスケジュールします。

しかし、その関数でネストされた を使用するfragmentedForことで、関数を非同期にすることになり、内部ループの反復がクロスファイアを開始します。

お気づきのように、終了するまで待機させる必要があります。つまり、前の反復のコールバックに次の反復をフックすることを意味します。fragmentedFor非同期反復ステップを処理する別のものを作成する必要があります。

function fragmentedForAsync(from, to, body, callback) {
    var i = from;
    (function loop() {
        if (i < to)
            body(i++, loop); // pass itself as callback
        else
            callback();
    })();
}

そして、次のように使用します。

fragmentedForAsync(1, 5, function(i, callback) {
    fragmentedFor(10, i*100, function(j) {
        console.log(j);
    }, function() {
        console.log('finished '+1);
        callback(); // next turn of outer loop
    });
}, function() {
    console.log('finished all')
});
于 2013-06-01T19:51:35.483 に答える