不确定层数的嵌套循环

[TOC]

不确定层数的嵌套循环

1、多层循环,铺平。

结果发现,其实我要实现的就是一个迭代器而已。我想在python里面应该有相应的成熟的方法。而我现在还在这…………重复造轮子。而且写这个代码,用了很长时间

简单的思路

想自定义一个对象,然后将相关的方法,封装在里面。结果写了一点,发现不是个好思路。还要考虑对象的初始化问题。js里面的对象,尤其是继承,用起来比较麻烦。所以,放弃了这种思路。

var list=[];
list.push(4);
list.push(3);
list.push(2);
list.push(1);

/*var iterator={
	cur:null,
	list:[],
	
	next:function(){
		var cur = this.cur;
		return cur;
	}
	
};*/


var cur=[];
for(var i=0;i<list.length;i++){
	cur.push(0);
}

思路面向过程的思路

面向过程,很多变量,都暴露出来了。下面是一个无限循环的写法。核心的代码,next。但下面是个无限的循环,没有结束。

list=[4,3,2,1];
cur =[0,0,0,0];
/***循环产生,没有结束****/
function next(){
	var idx = cur.length-1;
	var num = cur[idx];
	while(idx>=0){
		cur[idx]++;
		if(cur[idx]>=list[idx]){
			cur[idx]=0;
			idx--;
		}else{
			break;
		}
	}
	return cur;
}

思路面向过程的思路(改进)

在这个代码里面,增加了停止的判断。idx<0,说明第一位也进位了。那么,这个循环就要结束了。

/***返回false,即停止。但是,只是返回索引。没有返回数组****/
list=[4,3,2,1];
cur =[0,0,0,0];
function next(){
	var idx = cur.length-1;
	var num = cur[idx];
	while(idx>=0){
		cur[idx]++;
		if(cur[idx]>=list[idx]){
			cur[idx]=0;
			idx--;
		}else{
			break;
		}
	}
	return idx<0?false:cur;
}

闭包改进:将内部变量封装起来

使用了闭包,将内部变量封装起来。然后增加了一个pickCur方法,返回当前具体的内容。因为,我们最需要的就是pickCur返回的值,而不是数组的索引。(这个从使用上了来说,没有什么问题。但是这并不是个迭代器。而且上来,就没有第一个元素,所以,自己需要先处理第一个元素,然后不停的next,直到false。)

/***返回当前的数组元素,并利用了闭包****/
var arr=[[1,2,3,4],[1,2,3],[1,2],[1]];

function makeNext(arr){
	var  list=[],
		cur=[];
	for(var i= 0;i<arr.length;i++){
		list.push(arr[i].length);
		cur.push(0);
	}
	var pickCur=function(){
		var res=[];
		for(var i= 0;i<arr.length;i++){
			res.push(arr[i][cur[i]]);
		}
		return res;
	};
	return function(){
		var idx = cur.length-1;
		var num = cur[idx];
		while(idx>=0){
			cur[idx]++;
			if(cur[idx]>=list[idx]){
				cur[idx]=0;
				idx--;
			}else{
				break;
			}
		}
		return idx<0?false:pickCur();
	}
}
next=makeNext(arr);
next();//一路next 下去,直到遇到false则表示,遍历结束

迭代器(最终版)

最后发现,其实,我们需要的是一个迭代器,最开始的时候,我并没有想到真实需求。(有空研究一下python、js里面有没有相关的库)。此外,pickCur在迭代器模式下,也遇到一些挫折。下面先看成品。

/*************
 * 2019年7月13日
 * 当时还有一个问题,next实现了,当是没有第一个
 * 我想把它当个迭代器,python里面是否有相应的迭代器?
 *************/
var arr=[[1,2,3,4],[1,2,3],[1,2],[1]];

function makeNext(arr){
	var  list=[],
		cur=[];
	var  done=false;
	//初始化
	for(var i= 0;i<arr.length;i++){
		list.push(arr[i].length);
		cur.push(0);
	}
	//还是无法返回最后一个元素
	var pickCur=function(){
		var res=[];
		for(var i= 0;i<arr.length;i++){
			res.push(arr[i][cur[i]]);
		}
		/*********
		 * 解决最后一个无法返回的问题。
		 *********/
		if(done){  //先判断当前有无结束。停止了next移动
			res = false;
		}else{
			next();
		}
		return res;
	};
	var next = function(){
		var idx = cur.length-1;
		var num = cur[idx];
		while(idx>=0){
			cur[idx]++;
			if(cur[idx]>=list[idx]){
				cur[idx]=0;
				idx--;
			}else{
				break;
			}
		}
		if(idx<0){
			done = true;
		}
		//return idx<0?false:pickCur();
	}
	return pickCur;
}
next=makeNext(arr);
//调用方式
while(cur=next()){
	console.log(cur)
}

作为对比,写一下,pickCur的遇到的挫折。下面会使,最后一个无法产生。

var pickCur=function(){
		var res=[];
		for(var i= 0;i<arr.length;i++){
			res.push(arr[i][cur[i]]);
		}
		next();//往下执行一步。如果发现是最后一个,done为真。但在这次循环时,done已经被next循环了。
		return done?false:res;
	};

2、递归思路

在不确定嵌套循环的次数情况下,可以采用不停的递归,迭代。将每个循环的数组,变成一个函数。然后将所有的函数,放到一个数组里面。

talk is cheap,show me the code!!!

思维的挣扎

function listRun(arr,next){
	for(var i =0;i<arr.length;i++){
		next(arr[i])
	}
}

function print(v){
	console.log(v);
}
listRun([1,2,3,4],print)





arr=[[1,2,3,4],[1,2]];
function listRun(next,arr){
	for(var i =0;i<arr.length;i++){
		next(arr[i])
	}
}

function print(v){
	console.log(v);
}
listRun([1,2,3,4],print)
//期待如下使用
fn0 = listRun(arr[0])
fn1 = listRun(arr[1])
fn2 = listRun(arr[2])

fn0(fn1(fn2())) ?  不是这种调用形式

而是下面这种形式,所以next参数一定要存在
function listRun(arr,next){
	for(var i =0;i<arr.length;i++){
		next(arr[i])
	}
}

但是,外层循环,怎么知道内层的循环的内?比如next,也接受一个next。这样就无法正常写呢?是不是放在数组里面?
function listRun(arr,params,next){
	for(var i =0;i<arr.length;i++){
		params.push(arr[i]);
		next(arr[i])
	}
}

再改进。next不能要。因为不知道,不知道下一跳的next。
应该按如下来进行调用。
function listRun(arr1,params){
	for(var i =0;i<arr1.length;i++){
		params.push(arr1[i]);
		fn1(params)
	}
}

function listRun(arr2,params){
	for(var i =0;i<arr2.length;i++){
		params.push(arr2[i]);
		fn2(params)
	}
}

利用闭包,分步传入参数。
function listRun(arr1){
	return function(params){
		for(var i =0;i<arr1.length;i++){
			params.push(arr1[i]);
			fn1(params)
		}
	}
}
所以用的时候,大概是这样的:
fn0 = listRun(arr1);
fn1 = listRun(arr2);
//按下面串在一起?非常不好。
fn0(fn1(fn2()));


params一定要在每个函数中传递吗?未必,作为全局变量即可。
所以,我真正期待的是  fn=[fn1,fn2,fn3];
然后在用

3暴力循环代码

既然不确定要多少层循环,那我将所有可能的层数都给你写好了就可以了。然后根据你要选择的层数,给你返回一个合适的循环。

在写身份证的验证的时候,我会用到分组,即1-6位,区号,7-10,年,11-12,月,13-14日,15-17,三位顺序号,18验证码。然后对每个分组的可能进行循环一次。然后整体再次循环。

有点像,7块钱=5块+2块。利用小层循环拼接成大层循环。

loop1=loop([[1,2,3]]);
loop2 =loop([[4,5]]);
//按如下进行拼接
loop1(function(arr){
		loop2(function(arr2){
			//arr.splice(arr.length,0,...arr2);//合并数组
			var out=[];
			out.splice(0,0,...arr,...arr2);
			console.log(out);
		});
	})
上面的代码,还是不够优雅。能否像ramdajs函数,更优雅的合并呢?

核心代码:

function loop(arr){
	var fn1=function(fn){
		for(var i=0;i<arr[0].length;i++){
			fn([arr[0][i]]);
		}
	}
	var fn2 = function(fn){
		for(var i=0;i<arr[0].length;i++){
			for(var j=0;j<arr[1].length;j++){
				fn([arr[0][i],arr[1][j]]);
			}
		}
	}
	var fn3 = function(fn){
		for(var i=0;i<arr[0].length;i++){
			for(var j=0;j<arr[1].length;j++){
				for(var k=0;k<arr[2].length;k++){
					fn([arr[0][i],arr[1][j],arr[2][k]]);
				}
			}
		}
	}
	var fn4 = function(fn){
		for(var i=0;i<arr[0].length;i++){
			for(var j=0;j<arr[1].length;j++){
				for(var k=0;k<arr[2].length;k++){
					for(var l=0;l<arr[3].length;l++){
						fn([arr[0][i],arr[1][j],arr[2][k],arr[3][l]]);
					}
				}
			}
		}
	}
	var fn5 = function(fn){
		for(var i=0;i<arr[0].length;i++){
			for(var j=0;j<arr[1].length;j++){
				for(var k=0;k<arr[2].length;k++){
					for(var l=0;l<arr[3].length;l++){
						for(var m=0;m<arr[4].length;m++){
							fn([arr[0][i],arr[1][j],arr[2][k],arr[3][l],arr[4][m]]);
						}
					}
				}
			}
		}
	}
	var fn6 = function(fn){
		for(var i=0;i<arr[0].length;i++){
			for(var j=0;j<arr[1].length;j++){
				for(var k=0;k<arr[2].length;k++){
					for(var l=0;l<arr[3].length;l++){
						for(var m=0;m<arr[4].length;m++){
							for(var n=0;n<arr[5].length;n++){
								fn([arr[0][i],arr[1][j],arr[2][k],arr[3][l],arr[4][m],arr[5][n]]);
							}
						}
					}
				}
			}
		}
	}
	switch(arr.length){
	case 1:
		return fn1;
	case 2:
		return fn2;
	case 3:
		return fn3;
	case 4:
		return fn4;
	case 5:
		return fn5;
	case 6:
		return fn6;
	}
}
//测试
fn = function(arr){
	console.log(arr);
}
myloop=loop([[1,2,3,4],[1,2]]);
myloop(fn);

关于相关的合并函数,确实不好写,不知道如何写。


//包裹函数
function(arr){
	return function (loopFn){
		return function(callback){
			loopFn(function(arr2){
				var out=[];
				out.splice(0,0,...arr,...arr2);
				callback(out);
			});
		}
	}
}

//包裹函数,将loop2进行包裹。因为loop1函数不需要返回,所以下面都不行。
function joinFn(loopFn){
	return function (arr){
		return function(callback){
			loopFn(function(arr2){
				var out=[];
				out.splice(0,0,...arr,...arr2);
				callback(out);
			});
		}
	}
}

4、其他方法(eval、goto)

其他是否能用goto 等代替?

使用eavl来生成循环函数,然后进行循环。跟暴力循环又有点像。代码如下:


function getChar(n){
    var str='abcdefghijklmnopnrstuvwxyz';
    str+=str.toUpperCase();
    // console.log(str.length);
    var result=[];
    
    // console.log(index);1
    for(var i=0;i<n;i++){
        var index=i%str.length;
        var pb='';
        if(parseInt(i/52)>0){
            pb=str[parseInt(i/52)-1];
        }
        result.push(pb+str[index]);
    }
    return result;
}

function makeLoop(n){
    var tochar=getChar(n);
    var fn='fn([';
    var cmd='var fn6 = function(fn){';
    for(var i=0;i<n;i++){
        cmd+='for(var '+tochar[i]+'=0;'+tochar[i]+'<arr['+i+'].length;'+tochar[i]+'++){';
    }
    for(var i=0;i<n;i++){
       fn+='arr['+i+']['+tochar[i]+']';
       if(i<n-1){
           fn+=',';
       }
    }
    fn+='])';
    // cmd+= fn([arr[0][i],arr[1][j],arr[2][k],arr[3][l],arr[4][m],arr[5][n]]);
    cmd+=fn;
    for(var i=n-1;i>=0;i--){
        cmd+='}';
    }
    cmd+='};';
    console.log(cmd);
    return cmd;
}
//核心代码
eval(makeLoop(2));
console.log(fn6);

arr=[[1,11],[1,2],[3],[4],[5],[6],[7],[8],[9],[10]];
fn=function(v){
    console.log(v);
}
/* var fn6 = function (fn) {
    for (var a = 0; a < arr[0].length; a++) {
        for (var b = 0; b < arr[1].length; b++) {
            for (var c = 0; c < arr[2].length; c++) {
                for (var d = 0; d < arr[3].length; d++) {
                    for (var e = 0; e < arr[4].length; e++) {
                        for (var f = 0; f < arr[5].length; f++) {
                            for (var g = 0; g < arr[6].length; g++) {
                                for (var h = 0; h < arr[7].length; h++) {
                                    for (var i = 0; i < arr[8].length; i++) {
                                        for (var j = 0; j < arr[9].length; j++) {
                                            fn([arr[0][a], arr[1][b], arr[2][c], arr[3][d], arr[4][e], arr[5][f], arr[6][
                                                        g], arr[7][h], arr[8][i], arr[9][j]])
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};*/
fn6(fn);