High_Level_Skip_in_Matlab中的高端跳过循环
循环控制
Matlab的循环控制语句有两个,分别是for
和while
。for
循环是一种计数循环,while
循环是一种条件循环。在循环中,有时候我们需要跳过一些循环,这时候就需要用到continue
语句;当我们需要提前结束循环,这时候就需要用到break
语句或者return
语句。
for
循环
Matlab的for
循环基本语法如下。
1for column = Columns
2 % for each column of Columns, do the following
3 % ...
4 if needsToBreak
5 break
6 end
7
8 if needsToReturn
9 return
10 end
11
12
13 if needsToSkipFollowing
14 contine
15 end
16end
在循环过程中,有三种控制循环的方式,其中break
直接跳到end
的外面;return
(在函数里)直接跳出函数;continue
则忽略接下来到end
的所有代码,进入下一个列的循环。
值得注意的是,for
循环被循环数组的列。1:10
是一个行向量,所以for i = 1:10
循环10次,i
从1到10;如果for c = C
中C
是一个矩阵,那么就是循环C
的每一个列。
1for i = magic(3), disp(i), end
2 8
3 3
4 4
5
6 1
7 5
8 9
9
10 6
11 7
12 2
while
循环
这个循环语法则更为简单,也更加本质。
1while conditionExpression
2 if needsToBreak
3 break
4 end
5
6 if needsToReturn
7 return
8 end
9
10 if needsToSkipFollowing
11 contine
12 end
13end
首先检测表达式conditionExpression
是否为真,否则跳到对应的end
之后。在这个循环体重,同样可以采用break
/return
/contine
进行中断和跳过的操作。
这两种低级的操作太没有水准,今天我们必须高一点高端的跳过循环的操作。
高端的跳过之一
这个实现这样的功能,后台计算过程中,有一个进度条,提示计算的当前步骤,提供一个按钮来发送跳过当前步骤的提示,然后的命令窗口进行确认。
这个界面上有一个按钮,这个按钮的文字我还没找到办法修改,按照道理,应该可以找到一个Childeren,改掉那啥…
每次点击取消,就可以把控制权切换到命令行窗口,确认跳过,继续运行。
这个代码里面有几个有意思的函数:
waitbar
,产生一个等待进度条窗口,是传统基于Figure的界面的一部分setappdata
/getappdata
,把数据存储在图形对象中,用一个字符串作为索引,邪路commandwindow
把焦点放回命令行窗口,是个很实用的函数gcbf
返回回调函数调用的对象,比如delete(gcbf)
删掉回调函数对应的图形对象pause
,暂停运行若干时间,函数是一个浮点数,则为暂停的秒数
这里就是一个典型的while
循环用continue
跳过部分代码继续运行。从这里也可以看到,恰面的break
代码的判断部分,很容易就在while
的表达式中隐含。所以,break
在for
循环中是刚需,在while
中有可能简化合并。
1function skipExample
2
3cleanObj = onCleanup(@cleanAll);
4
5hWaitbar = waitbar(0, 'Iteration 1', 'Name', 'Solving problem', ...
6 'CreateCancelBtn', @(~,~)fcn);
7
8hWaitbar.CloseRequestFcn = @(~,~)cleanAll;
9
10setappdata(hWaitbar, 'skip', false);
11
12n = 3;
13i = 0;
14try
15 while(ishandle(hWaitbar))
16 if getappdata(hWaitbar, 'skip')
17 setappdata(hWaitbar, 'skip', false);
18 % for keyboard conformation to continue
19 fprintf("%d - skip this round, ", i)
20 fprintf("Press any key to continue...")
21 commandwindow; % switch to command window
22 pause % delete to just run through
23 fprintf("\n");
24 continue;
25 end
26 % pretent to calculate sth
27 pause(0.5)
28
29 i = i + 1;
30 waitbar( mod(i, n) / (n-1), hWaitbar, ['Iteration ' num2str(i)]);
31 end
32catch ME
33 fprintf("%s: %s\n", ME.identifier, ME.message);
34 cleanAll;
35end
36end
37
38function cleanAll
39delete(gcbf);
40end
41
42function fcn
43setappdata(gcbf, 'skip', true);
44end
这里的整个都很丑,还会因为这样那样的情况无法暂停导致窗口关不掉,用close all
也不行,因为,handleVisibility='off'
。这个时候就需要下面这个大杀器,干掉所有窗口,一旦你走上用Matlab编GUI程序的邪路,就会发现下面这两个语句的魅力!
1set(groot,'ShowHiddenHandles','on')
2delete(get(groot,'Children'))
太丑不能忍之后的考虑
因为要用同一个按钮(右上角$\times$)来完成两个职责:跳过和退出,造成了上面那个例子所有的混乱,窗口的关闭和句柄删除会干扰程序的逻辑。这就告诉我们,在设计GUI是一定要好好分析业务逻辑,一个界面元素(一组界面元素)应该有内聚性很高的职责,也就是只做一件事情。
出于这个考虑,我们又设计了下面的例子,这里,取消按钮和右上角的关闭按钮,都按照原有的语义,负责关闭进度条,取消/停止计算任务。那么跳过怎么办呢?我们不能用Ctrl-C来完成,因为这个快捷键有很高的优先级,会中止正在进行的计算。
那么我们设置一个快捷键,Ctrl-K来跳过一步计算。这里的关键就是给进度条设置监控键盘事件的回调函数。
1f.KeyReleaseFcn = @skipABeat;
2
3function skipABeat(src, evt)
4 if ismember('control', evt.Modifier) && evt.Key == 'k'
5 setappdata(src, 'skip', true);
6 end
7end
基本上,基于Figure或者基于UiFigure的GUI程序的回调函数都至少包含两个基本的参数,一个是发出消息的来源,一个是事件对象。对于键盘事件就是一个KeyData
数据结构:
1KeyData - 属性:
2
3 Character: 'k'
4 Modifier: {1x0 cell}
5 Key: 'k'
6 Source: [1x1 Figure]
7 EventName: 'KeyRelease'
上面这个对象,很容易得到,我们只需要运行这个,然后把焦点放在图窗上,按动键盘就可以得到实际拿到的事件对象是什么样子的。这是GUI编程中一个很重要的思路:因为系统可以运行,所以想要什么信息就要构造程序来获得。
1f = figure;
2f.KeyReleaseFcn = @(src, evt)disp(evt)
大概就是这样的,所以我们可以判断是否按下了k
,是否同时按着control
。
这个GUI就让人满意很多,因为概念上更加清晰,就不需要打那么多补丁。
1function skipExample3
2
3
4f = waitbar(0,'1','Name','Approximating pi...',...
5 'CreateCancelBtn','setappdata(gcbf,''canceling'',1)', ...
6 'Position', [0, 0, 480, 360]);
7
8
9movegui(f, 'center');
10
11f.KeyReleaseFcn = @skipABeat;
12
13setappdata(f,'canceling',0);
14
15% Approximate pi^2/8 as: 1 + 1/9 + 1/25 + 1/49 + ...
16pisqover8 = 1;
17denom = 3;
18valueofpi = sqrt(8 * pisqover8);
19
20steps = 20000;
21for step = 1:steps
22 % Check for clicked Cancel button
23 if getappdata(f,'canceling')
24 break
25 end
26
27 if getappdata(f, 'skip')
28 fprintf("Skip step = %d\n", step);
29 setappdata(f, 'skip', false);
30 continue
31 end
32
33 % Update waitbar and message
34 waitbar(step/steps,f,sprintf('%12.9f',valueofpi))
35
36 % Calculate next estimate
37 pisqover8 = pisqover8 + 1 / (denom * denom);
38 denom = denom + 2;
39 valueofpi = sqrt(8 * pisqover8);
40end
41
42delete(f)
43end
44
45
46function skipABeat(src, evt)
47 if ismember('control', evt.Modifier) && evt.Key == 'k'
48 setappdata(src, 'skip', true);
49 end
50end
总结
- 两种循环方式,
for
和while
break
/return
/continue
控制循环提前结束和跳过for
循环中使用break
更加自然while
循环中使用break
的条件可以合并到while
表达式中- 设计UI的过程中,一定要考虑清楚每个元素的职责,不要让一个元素负责多个职责
文章标签
|-->matlab |-->tutorial |-->continue |-->gui
- 本站总访问量:次
- 本站总访客数:人
- 可通过邮件联系作者:Email大福
- 也可以访问技术博客:大福是小强
- 也可以在知乎搞抽象:知乎-大福
- Comments, requests, and/or opinions go to: Github Repository