【问题标题】:Add elements to an already evaluated loop将元素添加到已评估的循环中
【发布时间】:2020-01-27 23:02:44
【问题描述】:

下午好,我遇到了以下问题,我正在尝试遍历带有子目录的列表,以将这些子目录的路由添加到数组(存储库)中,这是我的代码:

        for (n=0; n<=pendingRepos.length; n++){

        subruta = pendingRepos[pendingRepos.length -1]
           pendingRepos.pop()                   
            c.list(subruta, function(err, sublist) {
            if (sublist.length != 0){         
                for (g=0; g < sublist.length; g++){

                if (sublist[g].type === 'd' ){
                    repositories.push(subruta+'/'+sublist[g].name)
                    pendingRepos.push(subruta+'/'+sublist[g].name)
                    }      

                else {files.push(subruta+'/'+sublist[g].name)}                       
                }

            }

            });   

        }

例如,当为我的数组 pendingRepos 启动循环时,其结构如下:

pendingRepos = ['/ dir1 / dir2', / dir3 / dir4 ']

循环正确执行了 2 次并删除了最后一个元素,但在另一个循环向数组添加另一个“最后一个”元素时,第一个 for 循环没有考虑到它。 我知道在我添加更多元素之前已经评估了条件,这是正确的吗?如何避免?

【问题讨论】:

  • 您正在以一种奇怪的方式遍历pendingRepos,同时还使用.pop().push() 在循环内修改它。这似乎不是迭代该数组的可靠方法。
  • 另外,c.list() 似乎是非阻塞和异步的,因此您尝试并行运行一堆 c.list() 操作,并且所有操作都将在主 for 循环之后完成。这是完全不可预测的。我准备了一个解决这两个问题的答案,但后来决定我真的不知道你在迭代循环内正在修改的东西时试图用顶级循环迭代做什么。这对我来说毫无意义。
  • c.list() 中的c 是什么库?你能给它npm链接吗?
  • @TheHansinator npmjs.com/package/ftp 我正在使用这个模块
  • @FrancoGaluzzi 那么它是异步的(因为它正在联系 FTP 服务器)。我将在考虑到这一点的情况下完成对 JavaScript 的回答,但我会补充一点,如果您只是编写一个 shell 脚本而不是程序的一部分(这似乎很可能,因为您正在设置全局变量这个地方),我强烈建议使用类似 Powershell 的东西,因为与 JavaScript 不同,它可以让你编写命令等待服务器执行它的操作并回复你。

标签: javascript node.js loops


【解决方案1】:

看起来您正在以两种相互矛盾的方式处理待处理的存储库数组。外层for循环:

for (n = 0; n <= pendingRepos.length; n++) { ... }

pendingRepos 视为一个不可变列表,从头到尾遍历并处理每个元素。 (也没有正确执行此操作 - 如果这是我们正在使用的选项,我们应该迭代到 n &lt; pendingRepos.length)。

循环之后的逻辑,然而,

subruta = pendingRepos[pendingRepos.length -1]
pendingRepos.pop()

pendingRepos 视为一个可变堆栈,您可以从中继续处理最后一个元素,直到堆栈为空。

为了正确处理数组,您需要选择其中一个。由于您的代码的其余部分似乎正确地使用了堆栈方法,因此应更改顶部的循环以匹配,在这种情况下,它只是

while (pendingRepos.length > 0) { ... }

最终结果如下:

while (pendingRepos.length > 0){
    const subruta = pendingRepos[pendingRepos.length -1]
    pendingRepos.pop()                   
    c.list(subruta, function(err, sublist) {
        if (sublist.length != 0){         
            for (let g = 0; g < sublist.length; g++){
                if (sublist[g].type === 'd' ){
                    repositories.push(subruta+'/'+sublist[g].name)
                    pendingRepos.push(subruta+'/'+sublist[g].name)
                } else {
                    files.push(subruta+'/'+sublist[g].name)
                }
            }
        }
    });
}

编辑: 上述答案仅在 c.list() 是一个同步函数时才有效,该函数在返回之前立即运行您的回调 - 但是,由于它正在联系 FTP 服务器,因此不是。这意味着整个while 循环将在任何这些回调运行之前完成,并且它们添加到pendingRepos 的任何内容都不会被处理。为了使用异步函数,你必须以完全不同的方式构造你的函数,基本上尽可能使用越来越多的异步函数。

幸运的是,在这种情况下,这样做很容易。您对pendingRepos 所做的操作在概念上称为depth-first search (or "DFS"),您可以通过在每个子节点处重复搜索来搜索树结构。使用挂起目录的堆栈是进行 DFS 的一种方法,另一种方法是使用递归函数(基本上每次到达目录时都会重复搜索函数)。

这是一个可能的实现,使用一直延伸的回调。

// an outer function for the whole operation. You would provide
// a callback that takes the lists of repositories and files.
function getTheRepos(startList, callbackForWholeThing) {
    // build up our lists of repositories and files
    const repositories = [];
    const files = [];

    // keep track of how many calculations are running
    let repoGetCount = 0;

    // an inner function to run exactly one result
    function getOneRepo(subruta) {
        // at the start, say we're running
        repoGetCount++;
        c.list(subruta, function(err, sublist) {
            if (sublist.length != 0){         
                for (let g = 0; g < sublist.length; g++){

                if (sublist[g].type === 'd' ){
                    repositories.push(subruta+'/'+sublist[g].name)
                    // for each directory we find, call this inner function again.
                    // This is the critical part that makes this all work.
                    getOneRepo(subruta+'/'+sublist[g].name)
                } else {
                    files.push(subruta+'/'+sublist[g].name)
                }
            }
            // at the end, say we're not running,
            // and call the whole callback if we're the last one
            repoGetCount--;
            if (repoGetCount === 0) {
                callbackForWholeThing(repositories, files);
            }
        }
    });

    // now that we have the function, run it on each of our
    // start directories to start things off
    for (let n = 0; n < startList.length; n++) {
        getOneRepo(startList[n]);
    }

    // the cogs are in motion, so now return.
    // The callback will be called when the tree has been searched.
}

【讨论】:

  • 似乎还有一个问题,c.list() 可能是异步的,它失去了对循环条件或循环顺序的所有控制。
  • 非常感谢您提供的信息,但我仍然得到相同的结果,在 while 循环结束时,数组 'pendingRepos' 包含已添加但循环未重复的元素跨度>
  • @FrancoGaluzzi 那么它可能就像 jfriend00 所建议的那样 - c.list() 是一个异步函数,整个 while 循环将在任何回调发生之前运行。这意味着您需要以一种完全不同的方式来构建循环/函数,并考虑到这一点,而是使用递归函数。我将编辑问题以显示这一点。
  • 非常感谢,我被困在我的项目的这一部分,ftp 模块npmjs.com/package/ftp 的 c.list 函数结果返回给我一个包含远程位置目录的列表 my目的是列出该目录中的所有子目录,但我找不到解决此问题的方法
  • 谢谢,我会尝试用这个答案解决我的问题,很好的解释
猜你喜欢
  • 1970-01-01
  • 2021-05-14
  • 1970-01-01
  • 2018-09-26
  • 1970-01-01
  • 1970-01-01
  • 2019-03-31
  • 2019-04-02
相关资源
最近更新 更多