如何在Javascript中用递归替换for循环?

如何在Javascript中用递归替换for循环?,javascript,recursion,functional-programming,grid-layout,Javascript,Recursion,Functional Programming,Grid Layout,我有两个for循环,可以很好地创建包含行和列的网格,但是我想改进使用递归的解决方案,因为它更干净,更推荐(在函数式编程中) 所需的输出是css网格中使用的单个对数组 const createGrid = (rows,columns) => { let grid=[] for (let y = 3; y <= rows; y += 2){ let row = [] for (let x = 3; x <= columns; x +=

我有两个
for
循环,可以很好地创建包含行和列的网格,但是我想改进使用递归的解决方案,因为它更干净,更推荐(在函数式编程中)

所需的输出是css网格中使用的单个对数组

const createGrid = (rows,columns) => {
    let grid=[]
    for (let y = 3; y <= rows; y += 2){
        let row = []
        for (let x = 3; x <= columns; x += 2){
            let col = [y, x]
            row = [...row,col]
        }
        grid =[...grid, ...row]
    }
    return grid
}

const createGrid=(行、列)=>{
让网格=[]

对于(设y=3;y基元递归

这里有一种使用递归生成网格的可能方法-

constmakegrid=(f,rows=0,cols=0)=>
排
cols({xPos:x,yPos:y}),2,3)
log(JSON.stringify(g))
//[{“xPos”:1,“YPO”:1}
//,{“xPos”:1,“YPO”:2}
//,{“xPos”:1,“YPO”:3}
//   ]
//,[{“xPos”:2,“YPO”:1}
//,{“xPos”:2,“YPO”:2}
//,{“xPos”:2,“YPO”:3}
//   ]

//]
函数式编程更多地是关于高阶函数而不是直接递归。我相信下面的示例与您的示例相当,使用了underline.js中的
.range
、标准库中的
map
flatMap

const rowRange=\范围(3,行+1,2);
const colRange=u.range(3,列+1,2);
返回rowRange.flatMap(row=>colRange.map(col=>[col,row]);

首先,在阅读您的代码时,我认为您生成了一种样式的网格,因此
makeGrid(7,9)
将产生如下结果:

[
  [[3, 3], [3, 5], [3, 7], [3, 9]], 
  [[5, 3], [5, 5], [5, 7], [5, 9]], 
  [[7, 3], [7, 5], [7, 7], [7, 9]]
]
相反,它返回单个成对数组:

[[3, 3], [3, 5], [3, 7], [3, 9], [5, 3], [5, 5], [5, 7], [5, 9], [7, 3], [7, 5], [7, 7], [7, 9]]
我很确定我不是唯一的一个。Bergi在评论中建议了一个修正,将其改为前者。(这就是将
grid=[…grid,…row]
改为
grid=[…grid,row]
所能做的。)《谢谢》的精彩回答也是基于同样的假设

这是一个问题

当读者不能很快理解代码的功能时,维护代码就变得更加困难……即使是几周后的你自己

您可能会听到用递归替换循环的建议,原因与此相关。循环都是关于显式命令式指令,以获得所需的内容,这取决于变量的变化,然后您必须跟踪这些变量,并且很容易出现一个接一个的错误。递归通常更具声明性,这是一种表示结果为您所需的方式我们要寻找的只是将这些简单的结果与当前数据相结合,并指出如何通过基本情况或递归调用获得更简单的结果

然而,可读性和可理解性方面的优势是关键,而不是解决方案是递归的

不要误解我的意思,递归是我最喜欢的编程技术之一。Thankyou的回答是美好而优雅的。但它不是唯一能解决显式
for
-循环存在的问题的技术。通常,当我试图将初级程序员转移到中级及更高级程序员时,我首先要做的事情之一是重新编程lace
for
-使用更有意义的结构进行循环。大多数循环都试图做一些事情中的一件。它们试图将列表中的每个元素转换为新的(
映射
),尝试选择元素的一些重要子集(
过滤器
),尝试查找第一个重要元素(
查找
),或尝试将所有元素组合成一个值(
reduce
)。通过使用这些元素,代码变得更加明确

同样重要的是,从Thankyou的回答中可以看出,将可重用的代码片段分离出来,以便您的主要功能可以集中于重要的部分。下面的版本提取了一个函数
rangeBy
,它将
步骤
参数添加到我通常的
范围
函数中。
范围
创建一个整数范围例如,
range(3,12)
产生
[3,4,5,6,7,8,9,10,11,12]
rangeBy
添加一个初始
步骤
参数,从而
range(2)(3,12)
产生
[3,5,7,9,11]

我们使用
rangeBy
函数和
map
以及它的近亲
flatMap
来创建循环函数的更明确版本:

const rangeBy=(步长)=>(低、高)=>
[…数组(Math.ceil((hi-lo+1)/step))]
.map((_,i)=>i*step+lo)
const createGrid=(行、列)=>
rangeBy(2)(3行)。平面图(y=>
rangeBy(2)(3,列).map(x=>
[y,x]
)
)

console.log(createGrid(7,9))
除非您需要任意数量的维度,否则您的代码完全可以。您是如何得出使用递归而不是循环“更干净、更推荐”的结论的?您是否有其他例子,其中循环转换为递归,这改进了代码?使用递归并不总是“更干净和更推荐”-我认为你试图将错误的概念应用到你的问题上。这完全没问题。如果没有完整的上下文,很难判断这是否是一个有效的建议,因为我不知道你在读什么书,但只有在有意义时才应用递归。不,你不应该用递归解决方案替换每个
for loop
-使用更简单、更容易阅读和理解的方法-这是解决您试图解决的任何问题的最佳解决方案。@letele如果这只是一次学习经验:尝试将每个循环转换为一个单独的递归函数。或者可能先将内部循环重构为一个单独的函数。我想curry
f
(并从
makeRow
中省略
row
参数),使用
rowCount
/
columnCount
而不是
rows
/
cols
。注意,这解决了(对我来说更有趣的问题)创建一个完整的
mxn
元素数组,这是Bergi建议的,可能是想要的,但被拒绝了。原来的只是创建了一个成对的数组。谢谢。这