Linux C 编程技巧--利用有限状态机模型编程

我们知道,一般编写程序时都要画出流程图,按照流程图结构来编程,如果编写一个比较繁琐,容易思维混乱的程序时,我们可以利用有限状态机模型画出一个状态转移图,这样便可以利用画出的逻辑图来编写程序,简洁且不易出错。

      那什么是有限状态机是什么意思呢?百度百科上这样解释:

      有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

 

常见的计算机就是使用有限状态机作为计算模型的:对于内存的不同状态,CPU通过读取内存值进行计算,更新内存中的状态。CPU还通过消息总线接受外部输入设备(如键盘、鼠标)的指令,计算后更改内存中的状态,计算结果输出到外部显示设备(如显示器),以及持久化存储在硬盘。
电脑游戏设计中也经常使用有限状态机模型。以水果忍者游戏为例,游戏中水果的状态是有限状态,其运行轨迹是由模拟物理运动规律的计算公式运算而成的,一个香蕉抛起来后会按照抛物线运行,其每一帧位置变化都是一个状态的改变,状态改变通过计算公式来决定。当然作为游戏不会仅仅这么简单,如果这么简单就是动画了,游戏还有复杂的人机交互事件,比如用手在屏幕上“切”了水果,水果感知到这个事件后,会按照程序逻辑进入爆炸状态。
简单知道其定义是没有用的,在C编程中我们改如何应用呢?
例题1:去除一个字符串中连续的空格,即 H__el___lo 变成 H_el_lo;
我们拿到这道题时,可能会想到用getchar()依次取字符,当遇到空格时,继续下一个字符是否为空格,如果是则删除,如果不是则继续;那么问题来了,如果空格后面还有空格呢?如果还有很多空格呢?如果还有其他要求呢?这样很容易造成思维混乱,所以这时我们可以用到有限状态机模型,好像这样说很模糊啊,该怎么使用呢?利用这种方式,最后的就是利用好flag,即标识符;这样吧,我们来画一下这个模型:
这样看好像有点抽象,我来解释一下:
状态0  (flag = 1)若当前字符是空格,输出并跳转到状态1(flag = 1),如果是非空格,则打印字符;
状态1  (flag = 1) 若当前字符为非空格,则输出并跳转到状态0,若是空格,则不打印;
下面几个例题我尽量写得清楚;
我们按照这种逻辑来写程序,看看是不是比较方便,代码如下:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     char flag = 0;  
  6.     int ch;  
  7.     while((ch = getchar()) != EOF)  
  8.     {  
  9.         switch(flag)  
  10.         {  
  11.         case 0:  
  12.             if(ch == ' ')  
  13.                 flag = 1;  
  14.             putchar(ch);  
  15.             break;  
  16.         case 1:  
  17.             if(ch == ' ')  
  18.                 continue;  
  19.             flag = 0;  
  20.             putchar(ch);  
  21.             break;  
  22.         default:  
  23.             break;  
  24.         }  
  25.     }  
  26.     return 0;  
  27. }  

程序执行结果如下:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ gcc -o test test.c   
  2. fs@ubuntu:~/qiang/char1$ ./test  
  3. H  el   lo  
  4. H el lo  
效果很明显;
上面的例题还算简单吧,好像不用这个模型也可以是吧,那好,我们再来一题
例题2:除连续的空格但字符串中的连续空格不变,比如H_ _el__"wor___ld"___lo;
大家看看,如果直接写程序是不是很烦,一会就乱掉了,那我们还用这个模型来编写,还是先画图:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     char flag = 0;  
  6.     int ch;  
  7.     while((ch = getchar()) != EOF){  
  8.         switch(flag){  
  9.         case 0:  
  10.             if(ch == ' ')  
  11.                 flag = 1;  
  12.             if(ch == '"')  
  13.                 flag = 2;  
  14.             putchar(ch);  
  15.             break;  
  16.         case 1:  
  17.             if(ch != ' '){  
  18.                 flag = 0;  
  19.                 if(ch == '"')  
  20.                     flag = 2;  
  21.                 putchar(ch);  
  22.             }  
  23.             break;  
  24.         case 2:  
  25.             if(ch == '"')  
  26.                 flag = 0;  
  27.             if(ch == '\"')  
  28.                 ch = '"';  
  29.             putchar(ch);  
  30.             break;  
  31.         default:  
  32.             printf("error!\n");  
  33.             break;  
  34.         }  
  35.     }  
  36.     return 0;  
  37. }  

执行结果如下:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ ./char3  
  2. H  el  "wor   ld"   lo  
  3. H el "wor   ld" lo  

 
来个实际点的问题:
例题3::除单行注释
示例程序:
[cpp] view plaincopy
  1. /*****This is a program!*****/  
  2. #include <stdio.h>  
  3.   
  4. int main()  
  5. {  
  6.     printf("Hello world!\n");//Hello world!  
  7. }  

将这段程序中的单行注释去掉,继续画图:
编写程序如下:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main(void)  
  4. {  
  5.     char flag = 0;  
  6.     char ch;  
  7.     while((ch = getchar()) != EOF)  
  8.     {  
  9.         switch(flag)  
  10.         {  
  11.         case 0:  
  12.             if(ch == '/')  
  13.                 flag = 1;  
  14.             else  
  15.                 putchar(ch);  
  16.             break;  
  17.         case 1:  
  18.             if(ch == '/'){  
  19.                 flag = 2;  
  20.             }  
  21.             else{  
  22.                 putchar('/');  
  23.                 putchar(ch);  
  24.             }  
  25.             break;  
  26.         case 2:  
  27.             if(ch == '\n')  
  28.             {  
  29.                 flag = 0;  
  30.                 putchar(ch);  
  31.             }  
  32.             break;  
  33.         default:  
  34.             break;  
  35.         }  
  36.   
  37.     }  
  38. }  

执行命令:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ ./quzhushi1 < test.c >result.c  

注意命令中用到的重定向,将test.c文件重定向到quzhushi1 中,输出结果重定向到 result.c中
可查看result.c中:
[cpp] view plaincopy
  1. /*****This is a program!*****/  
  2. #include <stdio.h>  
  3.   
  4. int main()  
  5. {  
  6.     printf("Hello world!\n");  
  7. }  

单行注释被删除。
例题4:去除单行注释和多行注释
还是这个测试程序:
[cpp] view plaincopy
  1. /*****This is a program!*****/  
  2. #include <stdio.h>  
  3.   
  4. int main()  
  5. {  
  6.     printf("Hello world!\n");//Hello world!  
  7. }  

好吧,继续画图,图上比较抽象,大家可以自己思考一下
这次比较复杂,要有5个标识符
测试代码如下:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main()  
  4. {  
  5.     char ch;  
  6.     int flag = 0;  
  7.     while((ch = getchar()) != EOF)  
  8.     {  
  9.         switch(flag)  
  10.         {  
  11.             case 0:  
  12.                 if(ch == '/')  
  13.                     flag = 1;  
  14.                 else  
  15.                     putchar(ch);  
  16.                 break;  
  17.       
  18.             case 1:  
  19.                 if(ch == '/')  
  20.                     flag = 2;  
  21.                 else if(ch == '*')  
  22.                     flag = 3;  
  23.                 else  
  24.                 {  
  25.                     flag = 0;  
  26.                     putchar('/');  
  27.                     putchar(ch);  
  28.                 }  
  29.                 break;  
  30.           
  31.             case 2:  
  32.                 if(ch == '\n')  
  33.                 {  
  34.                     putchar(ch);  
  35.                     flag = 0;  
  36.                 }  
  37.                 break;  
  38.   
  39.             case 3:  
  40.                 if(ch == '*')  
  41.                     flag = 4;  
  42.                 break;  
  43.   
  44.             case 4:  
  45.                 if(ch == '/')  
  46.                     flag = 0;  
  47.                 else  
  48.                     flag = 3;  
  49.                 break;  
  50.         }  
  51.     }  
  52. }  

执行命令:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ ./quzhushi < test.c >result.c  

执行结果:
result.c
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main()  
  4. {  
  5.     printf("Hello world!\n");  
  6. }  

大家可以比较画的图与所写程序,这种方法可以使我们编写程序时有个很好的思路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402526.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux远程登录三种方式telnet,ssh,vnc

linux远程连接三种方式telnet&#xff0c;ssh&#xff0c;vnctelnet和ssh服务只能实现基于字符界面的远程控制&#xff0c;如果要基于图形界面进行远程控制&#xff0c;可以借助免费的VNC来完成。一、telnet连接1.首先进入终端&#xff0c;查看是否安装了telnet服务。linux默认…

大数据之Yarn——Capacity调度器概念以及配置

试想一下&#xff0c;你现在所在的公司有一个hadoop的集群。但是A项目组经常做一些定时的BI报表&#xff0c;B项目组则经常使用一些软件做一些临时需求。那么他们肯定会遇到同时提交任务的场景&#xff0c;这个时候到底如何分配资源满足这两个任务呢&#xff1f;是先执行A的任务…

C/C++经典面试题

面试题1&#xff1a;变量的声明和定义有什么区别 为变量分配地址和存储空间的称为定义&#xff0c;不分配地址的称为声明。一个变量可以在多个地方声明&#xff0c;但只能在一个地方定义。加入extern修饰的是变量的声明&#xff0c;说明此变量将在文件以外或在文件后面部分定义…

Java跳出多重循环

From: https://www.cnblogs.com/fastfn/p/9777067.html 场景&#xff1a;很多的时候需要做到跳出多重循环&#xff0c;而在Java中虽然后goto关键字&#xff0c;但是是保留字&#xff0c;并没有启用。而在处理分支结构的if...else,switch...case,好像都达不到想要的效果。 作为…

java基础集合简介Map(三)下

From: https://www.cnblogs.com/douyu2580860/p/8358768.html --Map接口简介 今天来看一看map集合&#xff0c;map映射接口&#xff0c;用于存放键值对&#xff0c;<key,value>&#xff0c;通过key来查找value,顾名思义key不能为空&#xff0c;唯一且不重复&#xff0c;不…

从getmemery()函数看内存管理、函数传参等一系列问题

在C 面试题目中&#xff0c;会经常出现getmemery()函数的改错题&#xff0c;比如下面这道题&#xff0c; 例一&#xff1a;代码如下&#xff1a; [cpp] view plaincopy #include <stdio.h> char *getmemery() { char p[] "hello world!"; …

Java中array、List、Set互相转换

From: https://www.cnblogs.com/yysbolg/p/9977365.html 数组转List String[] staffs new String[]{"A", "B", "C"}; List staffsList Arrays.asList(staffs);//注意: Arrays.asList() 返回一个受指定数组决定的固定大小的列表。所以不能做 a…

Apache Shiro 使用手册(三)Shiro 授权

授权即访问控制&#xff0c;它将判断用户在应用程序中对资源是否拥有相应的访问权限。 如&#xff0c;判断一个用户有查看页面的权限&#xff0c;编辑数据的权限&#xff0c;拥有某一按钮的权限&#xff0c;以及是否拥有打印的权限等等。 一、授权的三要素授权有着三个核心元素…

UVa 10026 - Shoemaker's Problem

题目大意&#xff1a;鞋匠有n个任务&#xff0c;第i个任务要花费ti天&#xff0c;同时第i个任务每耽误一天要有fi的罚金。求完成所有任务的最小罚金。 虽然知道是贪心&#xff0c;可是并不确定如何作贪心选择&#xff0c;只好“取经”了...假如有两个任务i和j&#xff0c;先做i…

在VS2012中实现Ext JS的智能提示太简单了

Visual Studio 2012太强大了&#xff0c;居然能自己会去提取Ext JS的类的属性和方法&#xff0c;从而实现只能提示。下面就来介绍一下实现这个功能。在Visual Studio 2012中随便创建一个Web项目&#xff0c;我创建了一个空的Web项目&#xff0c;目录结构如下图所示&#xff1a;…

mybatis 查询之神坑

先看一个示例&#xff1a; 数据表数据&#xff1a; mybatis类和查询语句&#xff1a; 1. 当UserInfoMap中所有字段(包含association)都为NULL的话&#xff0c;getUserInfo的返回结果是个null&#xff0c;即使查询的记录存在&#xff01;运行结果如下&#xff1a; 2019-06-26 …

微软万圣节文件

为什么80%的码农都做不了架构师&#xff1f;>>> http://www.aka.org.cn/Docs/halloween/halloweenDoc.html 微软万圣节文件 圣节文件在微软以外被用作称呼一系列来源可靠的备忘录&#xff0c;内容是微软总部用来对付开源软件&#xff08;特别是Linux&#xff09;的…

linux C 学习 简单字符串逆序输出

看了下网上的字符串逆序输出&#xff0c;都相对复杂&#xff0c;下面给一个简单的字符串逆序输出小程序实现: [cpp] view plaincopy #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int i; int n; …

【干货分享】流程DEMO-补打卡

流程名&#xff1a; 补打卡申请 业务描述&#xff1a; 当员工在该出勤的工作日出勤但漏打卡时&#xff0c;于一周内填写补打卡申请。 流程相关文件&#xff1a; 流程包.xml 流程说明&#xff1a; 直接导入流程包文件&#xff0c;即可使用本流程 表单&#xff1a; 流程&#xf…

2019年最流行的10个前端框架

From: http://blog.sina.com.cn/s/blog_18337e9c40102yt1x.html &#xfeff;2019年最流行的10个前端框架 从去年下半年开始&#xff0c;互联网行业慢慢进入寒冬&#xff0c;一些设计师也不得不重新找工作。关于求职这个事情&#xff0c;UI黑客之前写过一篇文章《面试了50多位…

Linux C 中断言assert()使用简介

assert()是一个调试程序时经常使用的宏&#xff0c;在程序运行时它计算括号内的表达式&#xff0c;如果表达式为FALSE (0), 程序将报告错误&#xff0c;并终止执行。如果表达式不为0&#xff0c;则继续执行后面的语句&#xff0c;它的作用是终止程序以免导致严重后果&#xff0…

SQL中group by的用法

group by即按照给定字段对结果集进行分组&#xff0c;从字面意义上理解就是根据“by”指定的规则对数据进行分组&#xff0c;所谓的分组就是将一个“数据集”划分成若干个“小区域”&#xff0c;然后针对若干个“小区域”进行数据处理。 group by的写法&#xff1a; 1.select 字…

Linux C 数据结构---链表(单向链表)

上一篇我们讲到了线性表&#xff0c;线性表就是数据元素都一一对应&#xff0c;除只有唯一的前驱&#xff0c;唯一的后继。 线性表存储结构分为顺序存储、链式存储。 顺序存储的优点&#xff1a; 顺序存储的缺点&#xff1a; 链表就是典型的链式存储&#xff0c;将线性表L &am…

前端学PHP之文件操作(认真读读)

前面的话 在程序运行时&#xff0c;程序本身和数据一般都存在内存中&#xff0c;当程序运行结束后&#xff0c;存放在内存中的数据被释放。如果需要长期保存程序运行所需的原始数据&#xff0c;或程序运行产生的结果&#xff0c;就需要把数据存储在文件或数据库。一般地&#x…