大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: 编程语言 修改

需求:输出“飞哥真帅”100遍啊100遍!


while

不要傻乎乎的复制粘贴100遍:

console.log("飞哥真帅");
^_^,程序员厌恶ctrl+c / ctrl+v !

使用 while 关键字:

while (true) {
    console.log("飞哥真帅");
}

这就是循环(loop),循环又被称之为“迭代(iterate)”,是指计算机反复执行一段的过程。

循环和分支一起,构成了现代主流编程语言面向过程的核心。

现在这样直接给true值,就是一个永远不会停止的循环。

死循环一般都是应该避免的,所以我们需要在while后面的()中可设定循环终止条件

----------------------

教学备忘2022年7月13日:下一期把循环和数组分别拆成两章,这里引入break/continue跳出循环的语法

----------------------

为了实现100遍(不要这样自恋,为了便于观察,改成5遍吧)的效果,我们通常要使用累加器/计数器

let i = 0;       //i就是累加器,常用的起始值为0
while (i < 5) {  //满足条件:继续循环;否则跳出
    i++;         //每循环一次,累加一次
    console.log("飞哥真帅");
}
@想一想@:按上面的代码,"飞哥真帅"会被输出几次?为什么?

注意:一定要仔细的确定循环中的边界值,比如:

  • i=0,还是 i=1
  • i<5,还是 i<=5
以及 i++ 的位置,这些都是非常容易出错(error prone)的地方。

i的再利用

i 除了在while中作为累加器使用,也可以作为普通变量使用,比如:

let i = 0;      
while (i < 5) {  
    i++;        
    console.log("第" + i + "次:飞哥真帅");
}

以及更复杂的功能,比如:

求和

取1+2+3+...+100的值:(不要用人脑代替电脑,想着什么(1+100)*100/2……)

使用循环的关键是:找到一个固定的运算规律,每次循环都这样运行……

  • 利用累加器 i 的自增特性
  • 用一个sum来存储累加结果
  • 每次循环都把当前 i 值累加到sum中
let i = 0,
    sum = 0;
while (i < 100 ) {
    /*let*/ sum += i; /*let的声明究竟该放在哪里?*/
    i++;    //i究竟该哪里?
}
console.log(sum);

断点演示:

  • 每次循环变化
  • 常见错误

条件断点

在循环中进行调试的利器,可以更精确的指定在何时击中断点。

使用方式:断点上右键 - edit breakpoint - 输入条件表达式:


do...while

和while非常类似,但它首先会在do里面执行一次:

do {
    console.log(i);
    sum += i;
    i++;
} while (i <= 100);
console.log(sum);


for

实际上更常用的是for循环,比如:

for (let i = 0; //迭代初始值(init)
	 i < 5;   //结束条件(expression)
	 i++        //步长(post-loop),每次循环之后运行
){
	console.log("第" + i + "次:飞哥真帅");
}

实际上就是把累加器i的声明和累加都放在了for后面的()里。

断点演示:for循环的运行过程

上面的求和运算就可以写出:

for (let i = 1; i <= 100; i++ ){
	sum += i;
}

如何选择?

  • 尽量使用for循环,因为:for循环的的写法更“紧凑”
  • 除非“不知道(及不必知道)”需要循环多少次


变量作用域

花括号还可以确定变量的作用域(scope),即声明的变量,只能在一个“特定的区域”使用:

从变量声明开始,到声明变量所在{}的结束为止(如果是声明在{}中的话):

{
	let sum = 3 + 2;
	{
		console.log(sum); //还是OK的
		//let sum = 5;  //重复声明,不行,仍在之前sum的作用域中
	}
}
//sum++;  //不行,超出sum作用域

但是,for循序中 i 的作用域,是在for循环体中:

for (let i = 0; i < 3; i++) {
	console.log(i);
}

console.log(i);  //不行,用不了啦

//两个for循环,都使用同名的i,但互不干涉
for (let i = 5; i < 10; i++) {
	console.log(i);
}

这也是推荐优先使用for循环的原因。@想一想@:使用while循环会怎么样?


数组

数组(array)是一种常用的数据结构(数据容器),里面有序的存放着多个元素

一维数组

这是最常用的数组。之前我们把变量想象成一盒子,那么现在数组就是一“”盒子:

声明的方法非常简单:

let numbers = [1, 9, 7, 3, 15, 5, 4, 6];

获取数组中的元素,或者给数组中元素赋值,需要使用方括号([]),并在其中指定该元素在第几位(下标)。

注意:且数组的下标都是从0开始的,所以:

numbers[0]  //第1个,值为1
numbers[1]  //第2个,值为9
numbers[2]=100    //将第三个元素值更改为100
numbers[8]=986    //新增加第9个元素,值为986

数组中一共有多少个元素可以用.length取出,比如:

numbers.length  //8

遍历

通过循环,依次的获得数组中的每一个元素,这被称之为“遍历”:
for (let i = 0; i < numbers.length; i++) {
    console.log(numbers[i]);
}

注意:i 的初始值和循环结束条件,这几乎已成“定式”,^_^

PS:JavaScript中并没有直接支持多维数组 ……


实例:找到某个数

需求:在数组中查找某个值,比如说 int[]{8,7,9,10,2,4,5} 中找到10。

@想一想@:有没有问题?

思路:把数组中的元素挨个挨个的拎出来,一个一个的和10进行比较。

在循环中使用条件判断,就可完成这个功能:
let seed = 7;
for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] == seed) {
        console.log("找到了……");  //找到了,怎么办? 
    }
}

注意:作为一个专业(professional)的程序员,源栈的同学们,这时候一定要注意

  • 根据用户需求编程,而不是根据自己的想法编程 —— 这就是业余玩家和专业人员的区别。
  • 尤其是在用户需求不明的情况下,千万要“想当然”的自己把需求给定下来,什么“肯定”“显然”……
  • !问清楚再做!!!

明确模糊的需求:

  1. 找到后输出:找到了,在数组中第n位
  2. 没找到输出:没找到
  3. 完了吗?还有没有其他模糊的需求?

没找到?

能不能这样?
            for (int i = 0; i < studentIds.Length; i++)
            {
                if (studentIds[i] == 10)
                {
                    Console.WriteLine($"找到了,在数组中第{(i + 1)}位");
                }
                else
                {
                    Console.WriteLine("没找到");
                }
            }

演示:错误效果

问题的关键:只有当所有的元素都查找了一遍都没找到,才算没找到。肿么办?

  • 方案(一)在循坏外部引入新的变量(hasFound=>index消除冗余)
    	let /*hasFound=false,*/ index=-1;    
    	
    	for (let i = 0; i < numbers.length; i++ ){
    		if(numbers[i]==155){
    			//hasFound = true;
    			index = i;
    		}
    	}
    	
    	if(/*hasFound*/ index > -1){
    		console.log("找到了,在第"+ (index+1) + "位");	
    	}else{
    		console.log("没找到");	
    	}
    这种方案的问题是引入了一个额外的变量。
  • 方案(二)在循坏中判断,当检查到最后一个元素时,才最终确定是否没找到……
    	for (let i = 0; i < numbers.length; i++ ){
    		if(numbers[i]==155){
    			console.log("找到了,在第"+ (i+1) + "位");	
    		}
    		else{
    			//已经是最后一个元素啦!
    			if(i == numbers.length-1){
    				console.log("没找到");	
    			}//else nothing		
    		}
    	}


跳出循

问题就在于当我们找到了要找的那个值后,还有必要继续循环比对么?断点演示……

关于性能

好代码三大指标:安全、性能、可维护性。

三者不可兼得!

排名不分先后,权重因地制宜。

性能问题是最隐蔽的,你不去测量,好像一切OK。

Stack Overflow的例子:访问量这么高的一个网站,为什么就只要这么几台服务器?

并不是他们用了什么了不起的技术(相反,他们还用了很多程序员看不起的ASP.NET和SQL Server,而且数据库不是流行的水平扩展而是垂直升级),而是他们的代码没有性能浪费

break和continue

假设我们的需求是只要找到一个就OK,不用管其他,那么我们就需要使用:(断点演示)

  • break:跳出循环
    	if(numbers[i]==15){
    		console.log("找到了,在第"+ (i+1) + "位");	
    		break;
    	}
  • continue:跳过本次循环,继续下一次循环。
    找出100以内所有不能被7整除的数
    	for (let i = 0; i < numbers.length; i++ ){
    		if( i == 3 ){
    			//break;
    			continue;
    		}
    		console.log(numbers[i]);
    	}
continue用得比较少,因为几乎所有continue都可以转化成break,而且更优雅,比如上述代码就可以:
	if(i!=3){
		console.log(numbers[i]);
	}

本质上,所有的需求都是而且只能是通过计算机的循环/分支实现的,同学们要逐步转换思维,学会用循环分支来实现各种编程需求。


找到最大值

要:我一眼望过去就知道了……想象有很多很多数据,你一眼望不到头的那么多,怎么办?

是不是只能:

  • 两两比较,用一支笔一张纸记下两个数中最大的一个;
  • 然后再用这个当前最大值和下一个元素比较;
  • ……

直到所有元素比对完毕?

let max = numbers[0];    //max存放最大值
for (let i = 0; i < numbers.length; i++) {
    if (max < numbers[i]) {
        max = numbers[i];
    }//else nothing
}
console.log(max);


循环的嵌套

输出这样一个三角形:

    *
   ***
  *****
 *******

这就需要用到循环的嵌套

关键思路:

  • 能输出4行*****
  • 能使用循环和拼接字符串确定一行输出多少个***
  • 找到每行输出*数量和行数之间的关系
	//i: 第i排;
	for (let i = 1; i <= 4; i++) {		
		let stars = "";
		
		//j: 多少个星
		for (let j = 0; j < 2*i-1; j++) {		
			stars = stars + "*";
		}
		
		console.log(stars);
	}

注意:按惯例,循环嵌套中的累加器变量名按 i,j,k……依次命名,不要乱写。


作业

  1. 分别用for循环和while循环直接输出:1,2,3,4,5 和 1,3,5,7,9
  2. 分别利用while和for循环,计算出1000+999+998+997+……+100的值
  3. 让电脑计算并输出:99+97+95+93+...+1的值
  4. 再想想我们“数组中查值”的练习,需求真的清晰了么?
  5. 声明一个数组ids,存放若干整数,利用:
    • while
    • for循环 i--
    输出数组中每个元素的值
  6. 声明一个数组odds,利用循环把100以内的能被3整除的数按从小到大的顺序存入其中
  7. 计算出odds数组中所有元素的和
  8. 同时/一次性找到并输出数组中的最大最小值
  9. 完成课堂中的等腰三角形输出;再利用循环,输出这么一个梯形:
        333
       4444
      55555
     666666
  10. 输出10000以内的所有“素数/质数”(只能被自己和1整除的数)
源栈培训 ASP.NET MVC Session
觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 编程语言 中继续学习:

我们的 特色

  • 先学习,后付费
  • 线上/线下,自由组合

更多了解

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧