簡體   English   中英

轉譯器之戰:突破嵌套 function,有 vs 無 throw

[英]transpiler battle: breaking out of nested function, with vs without throw

我剛剛完成了我的第一個(玩具)轉譯器的“版本 0”。 有用。 它將一串“偽 JavaScript”(帶有附加功能的 JavaScript)轉換為一串可運行的 JavaScript。 現在,我想改進它。

其他 SO 用戶可能最感興趣的工作區域是:編譯后的代碼(即,我的轉譯器的 output)沒有注意到前面一些 SO 問題的已接受答案中給出的編碼風格建議。 如果我手頭有第二個轉譯器,該編碼風格建議得到重視,我可以就哪個分支更有希望繼續開發做出明智的決定——我想比較這兩個分支的性能、開發時間需要,錯誤的數量等,並據此決定。

讓我告訴你我的轉譯器處理的“附加 JS 功能”:“嵌套返回”。 考慮像這樣的閉包/嵌套函數

function myOuterFunc(){
    ... code ...
    function innerFunc(){
        ... code ...
    }
    ... code ...
}

(請注意,上面的 '...code...' 應該包括所有可能的 JS 代碼,包括更多嵌套的 function 聲明,因此myOuterFunc不一定是innerFunc的直接父級)

在上述情況下,假設您希望從內部某處返回myOuterFunc的結果 - 不一定直接在內部 - innerFunc

實現“嵌套返回”后,您可以簡單地編寫

return.myOuterFunc result

這是(不可運行的)function 使用此功能並做一些有意義的事情的示例

function multiDimensionalFind(isNeedle, haystack) {
    // haystack is an array of arrays
    // loop (recursively) through all ways of picking one element from each array in haystack
    // feed the picked elements as array to isNeedle and return immediately when isNeedle gives true
    // with those picked elements being the result, i.e. the 'found needle'
    var LEVEL = haystack.length;
    function inner(stack) {
        var level = stack.length;
        if (level >= LEVEL) {
            if (isNeedle(stack)) return.multiDimensionalFind stack;
        } else {
            var arr = haystack[level];
            for (var i = 0; i < arr.length; i++) {
                inner(stack.concat([arr[i]]));
            }
        }
    }
    inner([]);
    return 'not found'
}

這是我的轉譯器自動生成的(可運行的)代碼(很明顯,注釋是后來添加/刪除的),然后是一些代碼測試,如果 function 做了它聲稱做的事情(它確實如此,因為你可以說服自己。 )

 ///////////// the function ///////////////// function multiDimensionalFind(isNeedle, haystack) { try { var LEVEL = haystack.length; function inner(stack) { var level = stack.length; if (level >= LEVEL) { if (isNeedle(stack)) throw stack; } else { var arr = haystack[level]; for (var i = 0; i < arr.length; i++) { inner(stack.concat([arr[i]])); } } } inner([]); return 'not found' } catch(e){ // make sure "genuine" errors don't get destroyed or mishandled if (e instanceof Error) throw e; else return e; } } ////////////////// test it ////////////////// content = document.getElementById('content'); function log2console(){ var digits = [0,1]; var haystack = [digits,digits,digits,digits,digits]; var str = ''; function isNeedle(stack){ str = str + ', ' + stack.join('') return false; } multiDimensionalFind(isNeedle, haystack); content.textContent = str; } function find71529(){ // second button var digits = [0,1,2,3,4,5,6,7,8,9] var haystack = [digits,digits,digits,digits,digits] function isNeedle(stack){ return stack.reduce(function(b,i){ return 10*b+i; }, 0) === 71529 // returns true iff the stack contains [7,1,5,2,9] } content.textContent = multiDimensionalFind( isNeedle, haystack ).join('_') }
 <button onclick='log2console()'>print binary numbers with 5 digits</button> <br> <button onclick='find71529()'>find something is 5d space</button> <div id='content'></div>

你可以在這里玩我的這個小提琴中的轉譯器。 我正在使用esprima 庫,即esprima之上的 escodegen.js 庫,這是我自己的一個正在進行中的微小的抽象語法樹生成庫(請參閱小提琴中的腳本標簽)。 不是庫的代碼,也不是 UI 代碼,即轉譯器的“真肉”不到 100 行(參見 function transpile )。 所以這可能沒有你想象的那么復雜。

我不記得我在哪里看到過風格推薦,但我可以肯定它實際上在多個地方。 如果您知道或遇到這樣一個問題,我邀請您將鏈接放入問題下方的評論中,我將標記為有用。 到目前為止,只有一個鏈接,謝謝 Barmar。

你可能會問為什么我什至費心先寫一個“不兼容”的轉譯器,而沒有立即為“兼容”版本編寫 go。 這與估計的工作量有關。 我估計“合規”版本的工作量要大得多 如此之多,以至於開始這樣的努力似乎並不值得。 我很想知道這種對工作量的評估是正確的還是錯誤的。 因此問題。 請不要暗示問題的修辭,甚至是不誠實的動機; 不管對某些人來說聽起來多么奇怪,我確實希望被證明是錯誤的,所以請不要以為我出於任何原因“只是這么說”,你會對我做一個不公正。 到目前為止,在我提出的所有問題中,這是迄今為止我投入最多的一個。 而且,如果你問我,這是迄今為止我在這里問過的最好的問題。

除了有人幫助我編寫轉譯器的“合規”版本之外,我還對任何客觀可證明的東西感興趣(盡管程度較低),這有機會說服我“不合規”的方式是錯誤的方法。 速度測試(帶有 jsperf 的鏈接)、可重現的錯誤報告,諸如此類。

我應該提到到目前為止我自己進行的速度測試:

第一次測試第二次測試

松散相關的問題

更好的方法是使用return (如果你不能完全重構塔)。 它清楚地說明了您在做什么以及何時從中間函數返回值; 您可以通過查看可能提供結果的那些函數來判斷。 相反,在那些中間層中使用throw是不可見的; 它以一種為錯誤條件設計的方式神奇地繞過它們。

如果您不想這樣做,我認為您除了throw之外沒有其他合理的選擇。 我想知道沿着生成器函數或類似函數的路線走下去,但我認為這只會使事情變得更加復雜,而不是更少。

使用return不會使示例代碼明顯復雜化,並且(在我看來)確實使正在發生的事情以及何時可能預期結果更清楚。

function wrapper(){
    function first(){
        function second(){
            function third(){
                doStuff4();
                some loop {
                    var result = ...
                    if (something) return result;
                }
            }
            doStuff2();
            let result = third();
            if (result) {
                return result;
            }
            doStuff3();
            return third();
        }
        doStuff1();
        return second();
    }
    return first() || "not found";
}

(在上面, result是真實性的;如果合適的話,用其他東西代替。)

好的,這是使用 JavaScript 的所有異步功能的另一種方法......所以基本上我已經重新創建了您的嵌套函數,但使用了 Promise/await 技術。 您只會得到一次結果,這是您第一次從任何級別的嵌套函數中解析它。 其他一切都將是GC。 一探究竟:

 // Create async function (async () => { const fnStack = (val) => { return new Promise((resolve, reject) => { // Worker functions. // Could be async; const doStuff1 = (val) => val + 1; const doStuff2 = (val) => val * 2; const doStuff3 = (val) => val * -1; // This will not affect result const doStuff4 = (val) => val + 1000; // Nested hell function first() { function second() { function third() { val = doStuff4(val); // Some loop for(let i = 0; i < 1000; i++) { if(i === 500) { // Here we got our result // Resolve it; resolve(val); } } } val = doStuff2(val); third(); // Below code will not affect // resolved result in third() above val = doStuff3(val); third(); } val = doStuff1(val); second(); } // first(); }). } // Run and get value const val = await fnStack(5); // We get our result ones console;log(val); })();

我認為你應該使用 arrays;

const funcs = [first, second, third];
for(let i = 0; i < funcs.length; ++i){
 const result = funcs[i]();
 if (result) break;
}

您應該只從具有結果的 function return

稍后,當我開始處理它時,我將在此處添加說明,說明如何使用我用於我的轉譯器的抽象語法樹生成庫,甚至可能從另一個版本開始,也許更詳細地解釋什么是事情是讓我認為這更多的工作。

舊版本如下(即將刪除)

如果該功能被多次使用,我們將需要這樣的東西:

function NestedThrowee(funcName, value){
    this.funcName = funcName;
    this.value = value;
}

return.someFunctionName someReturnValue (編譯前)將給出(編譯后)類似

var toBeThrown = new NestedThrowee("someFunctionName", someReturnValue);

在生成的catch塊內

if (e instanceof Error){
    throw e;   // re-throw "genuine" Error
} else {
    if (e instance of NestedThrowee){
    if (e.funcName === ... the name of the function to which
                       this catch block here belongs ...) return e.value;
    throw new Error('something happened which mathheadinclouds deemed impossible');
}

在一般情況下,有必要像上面顯示的那樣包裝結果(或 'throwee'),因為可能有多個,可能嵌套的“嵌套返回”,我們必須注意標語和catch類型的 object NestedThrowee匹配(按 function 名稱)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM