不确定层数的嵌套循环
[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);