米思齐(Mixly)图形化系列教程(六)-for循环
如果你用米思齐做过流水灯,大概率会遇到一个很尴尬的场景:5个LED要依次点亮,你就得从“控制”里拖出5个“数字输出”积木,挨个设置引脚号和高低电平,再串上5个延时。改一个灯的位置,后面全要跟着改。学生做课堂练习时,有人拖了十几个积木块,连他自己都看晕了。这时候我就特别想让他们认识一下for循环——这个藏在图形化积木里的“重复计数器”,学会了它,很多看似繁琐的活儿都能几块积木搞定。
要说清楚的是,这虽然是个图形化教程,但我建议你别把它只当“拖积木”来看。for循环在米思齐里就是C语言那句经典的for (int i = 1; i <= 10; i++)的图形化封装。理解了这个积木,你就等于同时学了一小半C语言基础,以后如果往Arduino代码方向走,这段知识能直接平移过去,不用再重新学一遍。这篇教程会从基本用法讲到嵌套循环,再讲到实际项目里最容易踩的坑,适合刚接触米思齐的初学者、带社团课的信息技术老师,以及想从图形化过渡到纯代码编程的爱好者。
1. 为什么图形化编程里还要单独学一个for循环
很多初学者会问:米思齐“控制”分类下不是已经有“重复执行”积木了吗?再整个for循环,感觉功能重复。这是最大的误解。两种积木处理的问题根本不一样。
1.1 “重复执行”和“for循环”积木的本质区别
“重复执行”积木干的事情很简单:把循环体里的内容固定重复N次。它不告诉你“现在到底是第几次”,就像一个只会闷头干活不报数的工人,你往里面放一块“点亮引脚3”的积木,它就老老实实把引脚3点亮N次,每次一模一样。如果你想让5个LED依次亮,用“重复执行”就得把5条不同的灯控积木都塞进循环体,然后一次性重复1遍,本质上就是把5块积木打包了一下,没有减少多少工作量。
for循环就不一样,它自带一个计数器。每循环一次,计数器就自动加一个数,而且这个计数器在循环体内部可以随时被读取和使用。我上课时喜欢打个比方:一块“重复执行”积木是一个只做俯卧撑的机器人,做10个就是10个;for循环则像体育老师喊“第1个、第2个、第3个……”——你不仅知道做了几个,还能根据这个“第几个”来调整动作。放到编程里,这个“第几个”就是循环变量,它让每一轮循环可以做出不一样的事情。
1.2 for循环解决的真实场景
哪些场景非它不可?我列几个最常见的:
- 逐个点亮一组LED:5个灯接到引脚3到7,循环变量从3变到7,正好对应引脚号。
- 连续读取传感器数据:要连续采样10次求平均值,循环变量可以用来做累加。
- 生成等差数列:0、0.1、0.2……一直到1,for循环配合步长可以做出来。
- 处理数组/列表数据:要从一串数据里找最大值、求和,必须知道“现在访问的是第几个数据”,for循环就是遍历数组的标准姿势。
- 舵机角度扫描:舵机从0度转到180度,每5度一个点,for循环可以控制角度值依次变化。
这些场景有一个共同特点:循环体里不仅要做“重复”的动作,还要用到一个不断变化的数。没有计数器,这些全做不了,这就是for循环存在的真正理由。“重复执行”负责“纯粹地重复”,for循环负责“带节奏地重复”。
1.3 图形化积木背后是真正的C语言逻辑
这也是米思齐和其他纯少儿编程工具不太一样的地方。米思齐最终会把积木生成Arduino的C++代码,你在界面右侧的代码区就能看到。for积木对应生成的代码非常直观:
for (int i = 1; i <= 10; i++) { // 循环体 }三个关键部分刚好对应积木上的三个参数:“从几开始”对应int i = 1,“到几结束”对应i <= 10,“每次加几”对应i++。我特别建议初学者用for循环时养成顺便瞄一眼代码区的习惯。积木拖多了容易眼花,但代码看起来反而清爽,而且你看多了就会发现,图形化里每个积木块基本就是代码里一条语句的“马甲”。把马甲脱掉,你就在学C语言了。
2. 米思齐里for循环积木的组成与基本用法
2.1 在哪个分类下找到它
打开米思齐(我用的是Mixly 2.0版,1.0版位置略有差异),左侧分类区找到“控制”,里面有一块写着“for循环 i 从 1 到 10,步长为 1”的积木,它的形状和“重复执行”一样,是一个C形槽的循环结构。把它拖到编程区,默认就是三个可直接修改的参数。
在Mixly 1.0里,积木文字可能是“变量i从1到10”步长放在一个单独下拉框里,整体布局稍有不同,但参数含义完全一样。版本差异的坑我放到后面单独讲。
2.2 三个关键参数:起始、结束、步长
这块积木默认是“i从1到10,步长为1”,运行后i会依次取1、2、3、4、5、6、7、8、9、10,总共执行循环体10次。你只需要做三件事:
- 修改起始值:循环变量从哪个数开始。
- 修改结束值:循环变量到哪个数为止。注意这里默认是“小于等于”的关系,也就是说
i <= 10时循环还会继续,i一旦大于10就停。 - 修改步长:每循环一次,循环变量加多少。默认是1,改成2就是i取1、3、5、7、9,跳过偶数。
举个实际例子。如果想循环5次,可以写“i从1到5”,也可以写“i从0到4”。两种写法在C语言里都很常见,前者直观,后者更贴近数组下标习惯(因为数组下标从0开始)。初学阶段我推荐先用1到N,直观不易出错,等用数组的时候再切到0到N-1。
有一点必须说清楚:步长不能填0。步长为0意味着循环变量永远不变,结束条件永远不满足,程序会卡死在循环里,表现出来就是板子上的程序“不响应了”。很多学生遇到过这种情况,一脸茫然,其实就是循环出不来。步长填负数也能让循环从大到小跑,这个后面讲递减循环时细说。
2.3 在循环体里使用循环变量
for循环最妙的地方是:循环体里的积木能读到“现在i等于几”。米思齐的for积木C形槽里可以拖其他积木进去,同时你可以在“变量”分类下找到一块写着“i”的椭圆形积木,把它拖到循环体里任意一个需要填数字的位置,比如数码管显示数字的输入框、串口打印的内容里、数字输出的引脚号选择器上。
举个例子:想实现“串口依次输出1到10”,只需要在for循环体里拖入“串口打印”积木,打印内容选择变量i,再加一个“串口打印换行”积木,上传烧录后打开串口监视器,就能看到1到10逐行打印出来。这个实验虽然简单,但它是理解循环变量的关键一步,我建议每个人都亲手跑一下。
2.4 入门案例:5个LED依次点亮
这个案例非常适合第一次上手for循环。
硬件准备:5个LED灯,分别通过220欧姆限流电阻接到Arduino Uno的数字引脚3、4、5、6、7,每个LED的负极接GND。
积木逻辑:
- 在“控制”分类中拖出“for循环 i从1到5”积木。
- 循环体里放一个“数字输出”积木,把引脚号设置成变量i,状态设为“高”。
- 再放一个“延时”积木,设1000毫秒。
- 循环体外面放一个“数字输出”积木,把引脚3到7全部置低……等等,这个外面其实做不了“全部”,这里就需要再次用到for循环,再拖一个相同的for循环,把数字输出设为“低”。
完整逻辑应该是:第一个for循环负责依次点亮5个灯,第二个for循环负责依次熄灭5个灯,形成一个“从左边亮到右边,再从左边灭到右边”的效果。
为什么引脚恰好可以用变量i?因为引脚号3、4、5、6、7就是一个步长为1的等差数列,i取1到5时,只要在数字输出的引脚参数里写“i+2”就能得到3、4、5、6、7(把循环变量和常量做加法运算,可在“数学”分类里找到加法积木)。这也是一种很常用的技巧:循环变量不直接等于目标值,而是和目标值差一个固定偏移量。
把这段积木切换到代码区,你会看到类似这样的内容(简化后):
for (int i = 1; i <= 5; i++) { digitalWrite(i + 2, HIGH); delay(1000); } for (int i = 1; i <= 5; i++) { digitalWrite(i + 2, LOW); delay(1000); }是不是比拖10块积木清爽多了。
3. for循环的步长扩展与嵌套结构
会用了基础写法之后,接下来该玩点花样了。实际项目里很少只用“从1到N”这种最规矩的循环,步长、方向、嵌套组合起来,才能应付复杂的控制逻辑。
3.1 步长不是1的等差数列
把步长从1改成2,循环变量取到的值就是1、3、5、7……如果想用偶数怎么办?从2开始,步长2,到10结束,就是2、4、6、8、10。这在控制RGB三色灯时非常常见,比如红色亮度从0到255,每次加5,得到的就是一个平滑渐变的亮度序列,如果用步长1,255次循环对肉眼来说基本没有区别,反而白白占用CPU时间。
步长越大,循环次数越少。举个例子:“i从1到10”跑10次,“i从1到10步长3”跑3次(i取1、4、7、10,第5次i变成13就停了)。很多新手以为步长改成3循环就结束在10上,其实结束值并总是能被步长整除,循环会在最后一次加步长后超过结束值而终止。理解了这一点,就不会对着2、5、8、11这种序列发愣了。
3.2 递减循环与倒计时
步长填负数就是递减循环。比如“i从10到1,步长为-1”,i依次取10、9、8……1。注意这里结束值就是1,条件是i >= 1时才继续,所以不能写反成“i从1到10步长为-1”,那样条件一开始就不成立,循环体一次也不执行。
我能想到的最直观的例子是数码管倒计时。把循环变量直接送到数码管显示,从9数到0,每次延时1秒,就是一个简易的“火箭发射倒计时”。实际项目中,递减循环还常用来做“从高到低扫描”,比如点阵屏从最后一行刷新到第一行。你不需要背语法,只要记住:起始值比结束值大,步长就是负数,千万别写成正数,否则一上来条件就为假,啥也不会发生。
3.3 循环嵌套:两层循环构建二维扫描
单个for循环是一维的,处理的是“一排灯”这种线性结构。遇到点阵屏、矩阵键盘这类二维结构,就需要循环嵌套——一个for循环套在另一个for循环里面。
嵌套循环的执行逻辑是:外层循环变量每加1,内层循环就要完整地跑一遍。我用下面这张表来展示外循环i从1到3、内循环j从1到4时的执行顺序:
| 外层i值 | 内层j取值 | 循环体执行次数 |
|---|---|---|
| 1 | 1、2、3、4 | 4 |
| 2 | 1、2、3、4 | 4 |
| 3 | 1、2、3、4 | 4 |
总共执行12次循环体。外层跑3圈,内层每圈跑4步,内层是“细节”,外层是“大节拍”。
嵌套循环最经典的应用是矩阵LED扫描。假设有一个4x4点阵,行接在引脚2到5,列接在引脚6到9。你要让每一行每一个点依次亮一下,可以这样思考:
- 外层循环控制行号,从2到5。
- 内层循环控制列号,从6到9。
- 循环体里先把当前行设为高、当前列设为低(或按点阵极性反接),延时一下,再全部关闭。
用代码表示就是这样:
for (int row = 2; row <= 5; row++) { for (int col = 6; col <= 9; col++) { digitalWrite(row, HIGH); digitalWrite(col, LOW); delay(2); digitalWrite(row, LOW); digitalWrite(col, HIGH); } }图形化里就是两个for积木套在一起,内层for整体放在外层for的C形槽内。嵌套这个事,看起来不难,但逻辑顺序特别容易搞混。我给学生的口诀是:“内层先跑完,外层才走一步。”反复念几遍,再结合表格里i、j的变化过程,基本就不会乱了。
3.4 嵌套循环里循环变量的组合使用
嵌套时两个循环变量不一定是独立的,内层循环完全可以使用外层循环变量的值。比如想打印九九乘法表,内层循环的结束条件就是外层i的当前值。在米思齐的图形化积木里,这意味着内层for积木的结束值不能直接填数字,而是要拖入变量i那块椭圆积木。
之前在5个LED例子里我们用了“i+2”这种偏移,其实这就是循环变量参与表达式计算的雏形。嵌套时这种用法更明显:外层i变化一次,内层j的值跟着i联动,形成三角形、梯形之类的扫描效果。如果两个循环变量彼此独立,是矩形扫描;如果内层依赖外层,就是三角形扫描。理解了这个,动态扫描类的项目就都不在话下了。
4. for循环里最容易踩的坑:delay、变量作用域与代码生成
这部分是写给正在做项目的人看的。图形化编程看起来不容易出错,但for循环有几个坑属于“积木拖得很漂亮,一运行就露馅”的类型,提前知道能省下大量排查时间。
4.1 循环体里放delay的两个隐患
初学者做LED闪烁,最常见的写法是:
for (int i = 0; i < 10; i++) { digitalWrite(13, HIGH); delay(1000); digitalWrite(13, LOW); delay(1000); }能跑,效果也对,但有两个问题在复杂项目里会格外明显。
第一个问题是实时性被破坏。整个循环跑完要20秒,这20秒内Arduino什么都干不了。如果程序里还有按键要响应、还有传感器要采集,按键按下时LED刚好在delay里,响应就会卡顿甚至“失灵”。为什么按键没反应?不是按键坏了,是程序还在延时里“睡觉”呢。图形化编程因为拖积木太方便了,容易让人忽略“代码是一条接一条顺序执行”这个基本事实。
第二个问题是delay耽误了循环本身。如果点亮5个灯,每个灯亮1秒然后马上切换,总时间5秒,这个没问题。但如果你想让灯快速闪动10次,而你每轮delay 500毫秒,那实际观感就是慢吞吞地闪,而不是“快速”了。delay直接决定了循环一次要多长时间,这是很多新手设置“次数”时没想清楚的。
我并不是说初学阶段完全不能用delay,而是说你要意识到它是在“阻塞”,教学演示可以,复杂项目就要考虑用“定时器扫描”这类非阻塞写法,那是后话,但意识得先有。
4.2 循环变量i在循环结束后还能不能用
这个问题在图形化里更容易被忽略,因为变量i一直挂在“变量”分类下,感觉随时都能用。但实际生成的代码是这么写的:
for (int i = 1; i <= 5; i++) { // 循环体 }这个int i是在for语句内部声明的,它的作用域严格限制在for循环块里。一旦循环结束,i这个变量在C语言层面就被“销毁”了,循环后面再想用i的值,编译器直接报错“i未定义”。
米思齐不同版本生成代码的方式略有差异,有的版本会把int i提升到setup或loop开头,这时候循环结束后还能读到i的值,但我强烈建议你别依赖这种行为。在图形化里我一般这样教:循环变量是循环的“临时工”,干完活就离开团队,不要指望它留下什么东西。如果循环结束后还要用最终值,就另外准备一个“记录用”的变量,在循环体里把值复制给它。这样做出的代码在哪个平台、哪种编译器下都不会出问题。
4.3 循环变量与“死循环”的关系
死循环是另一个高频坑。前面提到步长填0会卡死,其实还有一种情况:条件写反。比如你想从1数到10,积木却拖成了“i从1到10,步长为-1”,那循环一开始i等于1,条件i <= 10虽然成立,但步长是负数,i每次变小,它始终满足i <= 10,就永远跑不完。反过来,“i从10到1,步长为1”也是一样的效果,i从10往上加,条件i <= 1第一次就不成立。
判断死循环的关键就是看三个数之间的关系:起始值、结束值、步长的方向是否一致。起始值大于结束值时,步长必须是负的;起始值小于结束值时,步长必须是正的。两者一矛盾,不是一次都不跑就是永远不停。
4.4 善用代码区看懂积木到底干了什么
遇到循环问题,我最推荐的排查方式不是盯着积木看,而是点开代码区,看看米思齐把积木翻译成了什么。图形化积木的问题在于参数太多、层次嵌套时容易看花眼,代码区反而一目了然。括号、分号、大括号的缩进层级直接反映了嵌套关系。
比如嵌套循环,积木一层套一层,拖的时候很容易把内层for拖到外层for外面去了。代码区里两个嵌套大括号对齐得很清楚,一眼就能看出哪个循环在外、哪个循环在内。所以我做项目时的习惯是:每隔几步就切到代码区扫一眼,不需要完全读懂,只需看结构和变量名对不对。这个习惯对排查循环类问题特别有效。
5. 从for循环延伸到数组与复杂数据处理的进阶思路
学会基本循环、嵌套循环之后,for循环的威力才刚开始发挥。真正的编程场景里,for循环几乎总是和“数据集合”绑定在一起:当你有一堆数据要处理,for循环就是挨个访问它们的那只手。
5.1 累加、计数与求平均值
先来一个不带硬件、纯逻辑的经典练习:求1到100所有整数之和。图形化实现非常简单——在for循环外面放一个变量sum并初始化为0,循环体里放一块“sum = sum + i”的赋值积木,循环结束后sum就是5050。
为什么能把累加写成sum = sum + i?很多初学不明白这个等式。它不是数学方程,而是“把sum原来的值取出来,加上i,存回sum”。sum就像一个存钱罐,滚动循环每次往里面放一块钱。这个模式还可以扩展出很多变体:
- 求10次传感器读数的平均值:先累加,循环结束再除以10。
- 找出最大值:循环里比较当前i对应的值和之前的最大值,谁大就更新谁。
- 统计满足条件的个数:比如1到100里能被3整除的数有多少个,循环里加判断积木,满足条件就计数加1。
这些模式看起来很基础,但它们就是很多高级程序的骨架。比如PID控制里的误差累积、数据滤波里的滑动平均,本质都是“循环+累加”的变形。
5.2 用for循环遍历数组
米思齐的“变量”分类下有一组“列表”积木,对应C语言的数组。列表里的数据是有顺序的,第一个叫编号1(不同版本可能是编号0),第二个是编号2。要挨个访问列表里每一个数据,就用得上for循环。
图形化的处理方式一般是:拖一个for循环,循环变量i从1到列表长度;循环体里访问“列表的第i项”。比如存了5个温度值,想找出最高温,思路是:
- 先建一个变量max,初始化为列表第1项。
- for循环i从2到5,每次比较“列表第i项”是否大于max,是就把它赋给max。
- 循环结束后max就是最大值。
这个场景里,循环变量i扮演的角色是和结束值无关的“数据下标”。它不再是一串从1到5的普通数,而是指向数据位置的“指针”。理解这个职能转变,你就从“用循环做重复操作”进化到“用循环访问数据结构”了,这是编程思维的一个关键台阶。
5.3 PWM呼吸灯:循环变量到输出值的映射
for循环进阶还有一个非常典型的应用:呼吸灯。呼吸灯的原理是让LED的亮度从暗到亮再到暗循环变化,Arduino上通过PWM引脚输出不同的占空比实现。占空比数值范围一般是0到255。
图形化实现时,for循环可以让PWM值从0以步长5一路加到255,每次用analogWrite输出这个值,并延时20毫秒;然后再用一个步长为-5的循环从255减到0。这里for循环变量直接充当PWM的占空比参数,循环跑完一轮,灯就从暗到亮了一回。
如果你觉得0到255循环太直白,还可以尝试让循环变量不是直接输出,而是经过一次映射。比如用map函数把0到100的循环变量映射到0到255的PWM范围,这在图形化里就是数学分类下的“映射”积木。为什么会需要这种间接映射?很多时候循环变量只是一个“逻辑序号”,和它实际要控制的物理量之间并不是一比一的关系。学会在中间加一层映射,程序的灵活性会大幅提升。
6. 常见问题排查与自学/教学建议
最后这部分是我从实际课堂和项目里积累出来的“医保箱”。你们遇到的大多数for循环问题,归结起来其实就是那几类,我按症状给出排查思路。
6.1 问题:循环一次都没执行或者只执行一次
这个症状的根源基本在参数方向上。对照三个参数检查一下:起始值和结束值的关系与步长正负是否匹配。这是个习惯问题,不是智力问题,多检查几遍自然就有感觉了。
另一种“只执行一次”的情况是:循环体里没有用变量i,也没有写delay,程序瞬间跑完,看起来就像只执行了一次。其实循环执行了10次,只是太快了,肉眼根本分辨不出来。比如点亮5个LED后马上全部熄灭,中间没有延时,那5个灯会“同时闪了一下”,而不是依次点亮。判断方法是看代码区是否有循环语句,然后给循环体加一个几十毫秒的delay再做实验。
6.2 问题:LED没反应
如果循环看起来跑了,灯却不亮,一步步查:引脚号是否正确(Arduino Uno的PWM引脚是3、5、6、9、10、11,数字引脚0到13,别接在A0到A5上还想用数字输出点亮LED);LED正负极是否接反;限流电阻是否忘记了。排除硬件问题后,再检查数字输出积木里填的状态和循环变量是否含有意料之外的数字。
6.3 问题:找不到for循环积木或版本异常
如果你下载的米思齐版本里“控制”分类下没有for循环,或者代码区与你下载的教程截图不一致,多半是版本差异。米思齐官网提供多个版本,常见的是1.0系列和2.0系列,两者的积木外观和部分菜单位置有差别。1.0比较稳定,适合老教学资源;2.0界面更现代化,但部分教学资料还没跟上。还有的人下载的是“精简版”或第三方打包版,里面可能没有Arduino相关的完整分类,这时候直接去官网下载对应系统的完整版安装包,问题就解决了。
如果下载的版本里没有Arduino选项,还有一个可能:你下载的不是Arduino版本,而是针对其他硬件主控的版本(比如MicroPython、ESP32)。务必看清楚下载页面标注的适用主控类型,选择“Arduino”或“AVR”相关的包,才不会进去之后找不到熟悉的积木。
6.4 给老师/自学者的课堂教学建议
最后分享一点我自己的教学经验。学for循环,不要一上来就讲嵌套,要按台阶走:
第一步,先让学生用for循环做“数数”,串口打印1到10,理解循环变量就是“当前次数”。 第二步,做5个LED轮流点亮,把循环变量和引脚号关联起来。 第三步,改步长和方向,观察变化,比如倒计时、隔一个亮一个。 第四步,再做嵌套循环,用表格或手写笔记追踪i和j的变化过程。 第五步,才引入数组、映射这些进阶内容。
每步都配合真实的硬件效果,学生有反馈、有成就感,效率明显比空讲理论高。自学的朋友也可以按这个路径来,每完成一步回头看一次生成的C代码,会非常直观。
for循环这个知识点,在图形化编程里看起来不起眼,但它真是最能锻炼“程序思维”的一块积木。它会逼着你考虑变量的变化、循环的边界、条件的真假,而这些恰恰是编程入门的核心。现在你拖积木时脑海里能“预见”出那段C语言代码,那米思齐对你而言就不再是个玩具,而是通往更深入编程世界的一座桥了。