Transpiler之战:突破嵌套功能,带vs不带抛出 [英] transpiler battle: breaking out of nested function, with vs without throw

查看:61
本文介绍了Transpiler之战:突破嵌套功能,带vs不带抛出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚写完"version 0&",我的第一个(玩具)转运车.有用.它将字符串"pseudo JavaScript"转换为(具有附加功能的JavaScript)转换为可运行的JavaScript字符串.现在,我想改善它.

I have just finished writing "version 0" of my first (toy) transpiler. It works. It turns a string of "pseudo JavaScript" (JavaScript with an additional feature) into a string of runnable JavaScript. Now, I want to improve it.

其他SO用户可能最感兴趣的工作领域是:编译后的代码(即我的编译器的输出)不遵循对某些早期SO问题的公认答案中给出的编码样式建议.如果我需要第二个转译器来遵循该编码样式建议 ,那么我可以就哪个分支更有希望继续发展做出明智的决定-我想比较一下关于性能,所需的开发时间,错误数量等2个分支,并据此做出决定.

The work area possibly most interesting for other SO users is this: The compiled code (i.e., output of my transpiler) does not heed a coding style recommendation as given in an accepted answer to some earlier SO question. If I would have at my hands a second transpiler where that coding style recommendation is heeded, I could make an informed decision regarding which branch is more promising to continue to develop on - I'd like to compare the 2 braches regarding performance, development time needed, amount of bugs, and so on, and decide based on that.

让我告诉您有关附加JS功能"我的翻译员处理:嵌套回报".考虑像这样的闭包/嵌套函数

Let me tell you about the "additional JS feature" my transpiler deals with: "nested return". Consider closures / nested functions like so

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

(请注意,上面的"...代码..."应该包括所有可能的JS代码,其中包括更多的嵌套函数声明,因此myOuterFunc不一定是innerFunc的直接父代)

(note that above '...code...' is supposed to include every possible JS code including more nested function declarations, so myOuterFunc is not necessarily the direct parent of innerFunc)

在上述情况下,假设您希望从内部myOuterFunc 的某个地方返回结果-不一定是内部innerFunc

In above situation, suppose you desire to return a result from myOuterFunc from somewhere inside - not necessarily directly inside - innerFunc

带有嵌套回报";实施后,您可以编写简单的

With "nested return" implemented, you could then write simply

return.myOuterFunc result

这是使用此功能并执行有意义的操作的典范(不可运行)

Here is an exmalpe of a (not-runnable) function using this feature and doing something meaningful

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'
}

这是我的编译器从中自动生成的(可运行的)代码(很明显,注释是在以后添加/删除的),然后进行一些代码测试,以确定该功能是否按要求进行(并且可以做到)说服自己.)

And here is the (runnable) code my transpiler automatically produces from that (comments were added/remove later, obviously), followed by some code testing if that function does what it claims to do (and it does, as you can convince yourself.)

///////////// 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库 escodegen.js 库,这是我自己的一个很小的正在进行中的抽象语法树生成库(请参见小提琴中的script标签).不是库的代码,也不是UI代码的代码,即真实的肉类", Transpiler中的少于100行(请参见功能transpile).因此,这可能比您想象的要复杂得多.

You can play around with my transpiler in this fiddle here. I'm using the esprima library, the escodegen.js library on top of esprima, a teeny tiny work in progress abstract syntax tree generation library of my own (see the script tags in the fiddle). The code which is not library, and not UI code, i.e. the "real meat" of the transpiler has less than 100 lines (see function transpile). So this might be a lot less complicated than you thought.

我不记得在哪里看到过样式推荐,但是我可以肯定它实际上在多个地方.如果您知道或遇到一个这样的问题,请您在这个问题下的评论中加入友善的链接,我将标记为有用.到目前为止,只有一个链接,谢谢Barmar.

I can't remember where I had seen the style recommendation, but I am certain that it was actually in multiple places. If you know or come across one such question, I invite you to be so kind to put the link into a comment under the question, I will mark useful. So far, there is one link, thank you Barmar.

您可能会问,为什么我甚至不愿意写一个不合规"的论文?首先是编译器,并且没有选择符合"标准.版本马上.这与估计的工作量有关.我估计符合"标准的工作量要大得多.版本.如此之多,以至于进行这样的努力似乎并不值得.我非常想知道这项对工作量的评估是正确还是错误.这样的问题.请不要在这个问题上夸夸其谈,甚至是不诚实的动机;但是,听起来有些奇怪,确实是我想证明自己是错的情况,因此请不要以为我只是在说我只是说那个"而已.不管出于什么原因,你都会对我不公正.到目前为止,这是我提出的所有问题中,到目前为止,我所投入的工作最多.而且,如果您问我,这是迄今为止我在这里问过的最好的问题.

You might ask why I even bothered to write a "non-compliant" transpiler first, and did not go for the "compliant" version straight away. That has to do with the estimated amount of work. I estimate the amount of work to be much larger for the "compliant" version. So much so, that it didn't really seem worthwhile to embark on such an endeavor. I would very much like to know whether this assessment of the amount of work is correct or incorrect. Thus the question. Please do not insinuate a rhetorical, or even a dishonest motive for the question; however weird it might sound to some, it really is the case that I'd like to be proven wrong, so please be so kind not to assume I'm "just saying that" for whatever reason, you'd be doing me an injustice. This is, of all the questions I asked on SO so far, by far the one I've put the most work into. And, if you ask me, it is by far the best question I have ever asked here.

除了有人协助我撰写符合要求"译者的版本,我也对(客观上可以证明的)任何事物感兴趣(尽管程度较小),这有机会使我相信​​不合规"是什么意思.方式是错误的方式.速度测试(带有jsperf的链接),可重现的错误报告以及类似的内容.

Apart from someone assisting me in writing the "compliant" version of the transpiler, I'm also interested (albeit to a lesser degree) in anything objectively demonstrable which stands a chance to convince me that the "non-compliant" way is the wrong way. Speed tests (with links to jsperf), reproducible bug reports, things of that sort.

我应该提到到目前为止我自己进行的速度测试:

I should mention the speed tests I have done myself so far:

首次测试 松散相关的问题

推荐答案

更好的方法实际上是使用return(如果您不能完全重构塔).它使您清楚自己在做什么以及何时从中间函数返回值.您可以通过查看可以提供结果的功能来判断.相反,在这些中间层中看不到使用throw.它以一种针对错误情况设计的方式神奇地绕过了它们.

The better way really is to use return (if you can't completely refactor the tower). It makes it clear what you're doing and when you're returning a value from the intermediate functions; you can tell by looking at those functions where the result may be provided. In contrast, using throw is invisible in those intermediate layers; it magically bypassing them in a way designed for error conditions.

如果您不想这样做,我认为除了throw之外,您没有其他合理的选择.我想知道是否要沿生成器函数或类似函数的路线走下去,但我认为那只会使事情复杂化,而不是更少.

If you don't want to do that, I don't think you have a reasonable alternative other than throw. I wondered about going down the route of generator functions or similar, but I think that would just complicate things more, not less.

使用return不会使示例代码明显复杂,并且(在我看来)确实使正在发生的事情和可能预期的结果变得更加清楚.

Using return doesn't markedly complicate the example code, and does (to my eye) make it clearer what's going on and when results are potentially expected.

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经过了真实性测试;如果合适,请替换其他内容.)

(In the above, result is tested for truthiness; substitute something else if appropriate.)

这篇关于Transpiler之战:突破嵌套功能,带vs不带抛出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆