Python 最小公倍数算法

Python 最小公倍数算法

Python 最小公倍数算法 Python3 实例

以下代码用于实现最小公倍数算法:


# Filename : test.py
  
# 定义函数 def lcm(x, y):
    
#  获取最大的数
    if x >y:
 
  greater = x
    else:
 
  greater = y
     while(True):
 
  if((greater % x == 0) and (greater % y == 0)):
 
  lcm = greater
 
  break
 
  greater += 1
   

return lcm  
# 获取用户输入 
 num1 = int(input("输入第一个数字: ")) 
 num2 = int(input("输入第二个数字: "))
print( num1,"和", num2,"的最小公倍数为", lcm(num1, num2)) 

执行以上代码输出结果为:

 输入第一个数字: 54 输入第二个数字: 24 54 和 24 的最小公倍数为 216 

Python 最小公倍数算法 Python3 实例

Python 最大公约数算法

Python 最大公约数算法

Python 最大公约数算法 Python3 实例

以下代码用于实现最大公约数算法:


# Filename : test.py
  
# 定义一个函数 def hcf(x, y):
    """该函数返回两个数的最大公约数"""
    
# 获取最小值
    if x >y:
 
  smaller = y
    else:
 
  smaller = x
     for i in range(1,smaller + 1):
 
  if((x % i == 0) and (y % i == 0)):
 
  hcf = i
   

return hcf  
# 用户输入两个数字 
 num1 = int(input("输入第一个数字: ")) 
 num2 = int(input("输入第二个数字: "))
print( num1,"和", num2,"的最大公约数为", hcf(num1, num2)) 

执行以上代码输出结果为:

 输入第一个数字: 54 输入第二个数字: 24 54 和 24 的最大公约数为 6 

Python 最大公约数算法 Python3 实例

10分钟了解无人机飞控3大算法

10分钟了解无人机飞控3大算法

10分钟了解无人机飞控3大算法

关于飞控、不同的厂商在软件和硬件架构上都是有差别的,但是飞控的软件算法都离不开捷联惯导、卡尔曼滤波和PID控制这三种算法。

 

 

10分钟了解无人机飞控3大算法

我们先说捷联惯导,惯导也叫作惯性导航。说到导航我们会想到GPS导航,我们在生活中用的比较多,GPS是通过接收卫星发送的信号计算出自身位置的,但是当GPS设备上方被遮挡后,GPS设备就不能定位了。比如在室内、隧道内、地下等场所,基本收不到GPS信号。还有一种导航方式是不依赖外界信息的,这种导航叫作惯性导航。它在飞行器、车、船、潜航器等设备中都有应用。

 

 

10分钟了解无人机飞控3大算法

那什么是惯性导航,它就是利用载体上的加速度计、陀螺仪这两种惯性元件,去分别测出飞行器的角运动信息和线运动信息,与初始姿态、初始航向、初始位置一起交给计算模块,由计算模块推算出飞机的姿态、速度、航向、位置等航参数的自主式导航方法。惯性导航系统工作时不依赖外界信息,也不向外界辐射能量,不易受到干扰,是一种自主式导航系统。

 

 

10分钟了解无人机飞控3大算法

惯性导航系统 分为平台式惯性导航和捷联式惯性导航,早期的惯性导航系统都是平台式的,平台式惯导有实体的物理平台,陀螺仪和加速度计置于由陀螺稳定的平台上,该平台跟踪导航坐标系,以实现速度和位置解算,姿态数据直接取自于平台的环架;它的优点是直接模拟导航坐标系,计算比较简单;能隔离载体的角运动,系统精度高。它的缺点是结构复杂,体积大,制作成本高。

还有一种捷联式惯性导航(strap-downinertialnavigation),捷联(strap-down)的英语原义是“捆绑”的意思。因此捷联式惯性导航也就是将惯性测量元件,包括陀螺仪和加速度计,直接装在需要姿态、速度、航向等导航信息的主体上,用计算机把测量信号变换为导航参数,它的优点是没有平台,架构简单,体积小,维护方便,缺点是惯性元件直接装在载体上,环境恶劣,对元件要求较高;坐标变换中计算量大。总体来看,捷联惯导比平台式惯导优势要明显。

1969年,捷联惯导系统作为阿波罗“-13号登月飞船的应急备份装置,在其服务舱发生爆炸时将飞船成功地引导到返回地球的轨道上时起到了决定性作用,成为捷联式惯导系统发展中的一个里程碑

10分钟了解无人机飞控3大算法

卡尔曼滤波算法是卡尔曼等 人在20世纪60年代提出的一种递推滤波算法。它的实质是以最小均方误差为估计的最佳准则,来寻求一套递推估计的算法。这套算法采用信号与噪声的状态空间模型,利用前一时刻地估计值和现时刻的观测值来更新对状态变量的估计,求出现时刻的估计值,在惯性导航系统中有非常广泛的应用。刚才说的噪声指的是计算得出的值与实际值的误差。

那么为什么Kalman滤波会应用到惯性导航系统中呢?这主要是因为惯性导航系统的“纯惯性”传感器不足以达到所需的导航精度,为了补偿导航系统的不足,常常使用其他导航设备来提高导航精度,以减小导航误差。所以利用Kalman滤波算法,可以将来自惯性导航系统与其他导航装置的数据(如惯性导航系统计算的位置对照GPS接收机给出的位置信息)加以混合利用,估计和校正未知的惯性导航系统误差。

卡尔曼滤波算法广泛应用已经超过30年,包括机器人导航,控制, 传感器数据融合甚至军事方面的雷达系统以及导弹追踪等等。

比如,在雷达中,人们感兴趣的是跟踪目标,但目标的位置、速度、加速度的测量值往往在任何时候都有噪声。卡尔曼滤波利用目标的动态信息,设法去掉噪声的影响,得到一个关于目标位置最优的估计。这个估计可以是对当前目标位置的估计(滤波),也可以是对于将来位置的估计(预测),也可以是对过去位置的估计(插值或平滑)

 

 

10分钟了解无人机飞控3大算法

卡尔曼滤波算法是一个非常复杂的计算,我们结合飞行器来简单的讲一下它的计算过程,比如飞行器想知道自己的一个状态,这个状态可以是姿态、速度或位置等信息,我们知道飞行器的传感器是可以得到这些信息的,通过惯性导航的数学模型也可以计算出这些信息,但这两个信息的值与实际值还是有一定的差距的,把这两个值放在若干数学公式里可以得到一个最优值,通过这个最优值与传感器和数学模型的值进行对比,我们可以知道哪个值与最优值比较接近,下次的计算我们应该较多的参考接近最优值的那个值,比如传感器的值最接近最优值,那我们就把传感器的值使用较大的权重,数学模型得到的值加使用较小的权重,所得到的这个权重不是随便给的,也是通过数学公式得也来的。那我下一个时间段再计算的时候这个权重就要起作用了,传感器的值和数学模型的值会带着权重放在数学公式里得到最优值,然后我们再把这个最优值与传感器和数学模型的值进行对比,再看一下哪个值与最优值接近,如果还是传感器的值比较接近,我们还是会通过公式生成一个权重,交给下个时间段的计算使用。通过不断重复这样的计算,我们就可以得到一个相对较优的值,这就是卡尔曼滤波算法的大概过程。

 

 

10分钟了解无人机飞控3大算法

目前,人们通过科学研究获得了诸多具有优异控制效果的算法和理论,但在工程应用领域,基于经典PID的控制算法仍然是最简单、最有效的控制方案。

PID控制器是一种线性控制器,它主要根据给定值和实际输出值构成控制偏差,然后利用偏差给出合理的控制量。

目前主流的几款开源飞控中,无一例外地都是采用PID控制算法来实现无人机的姿态和轨迹控制。

PID里的PProportion的首字线,是比例的意思,IIntegral的首字线,是积分的意思,DDifferential的首字母,是微分的意思。

那么PID控制器算法能解决什么问题呢?以多旋翼为例,在没有控制系统的情况下,直接用信号驱动电机带动螺旋桨旋转产生控制力,会出现动态响应太快,或者太慢,或者控制过冲或者不足的现象,多旋翼根本无法顺利完成起飞和悬停动作。为了解决这些问题,就需要在控制系统回路中加入PID控制器算法。在姿态信息和螺旋桨转速之间建立比例、积分和微分的关系,通过调节各个环节的参数大小,使多旋翼系统控制达到动态响应迅速、既不过冲、也不欠缺的现象。

 

 

10分钟了解无人机飞控3大算法

下面我们简单的举个例子让大家了解一下PID的作用,我们先以一个自动驾驶的小车来举例子,为什么用小车呢而不用多旋翼来举例子呢?因为用小车举例,可以看到小车的行进轨迹,根据轨迹我们可以很直观的看到PID对控制的影响,便于理解PID的作用。

我们先讲P,比例控制。现在我们想让这个小车沿着绿线向前走,我们给P设置一个固定的值,这个值可以让离开绿线的小车向绿线的方向行驶,离的越远,方向盘打的角度越大,离的越近,方向盘打的角度越小。橙色的箭头表示小车行进的方向

 

 

10分钟了解无人机飞控3大算法

比如这个小车在这个位置,我们设置了一个中等大小的P值,想要沿着绿线走,在比例控制下路径是这样的,因为小车有一定的速度,到达绿线时因为惯性的原因又向前运动了,然后再根据中等P值向回打方向盘,当到达绿线时,同样因为惯性的原因冲过了头,因为小车越接近绿线,方向盘打的越小,所以小车每经过一次绿线,它的偏差就越来越小,所以随着小车多次的往复运动,就能离绿线越来越近,理论上最终能够行驶在绿线上面。

 

 

10分钟了解无人机飞控3大算法

如果我们把P值设置的比较大,它的路径是这样的,因为方向盘打的角度比较大,所以小车比中等P值的时候较早的到达绿线,但同样因为惯性的原因会多次往返绿线的两侧,也是一次比一次接近绿线,因为它方向盘角度打的比较大,所以会比中等P值往返的次数要多,最终经过多次往返,理论上它会离绿线越来越近,最终到达绿线上方。

 

 

10分钟了解无人机飞控3大算法

如果我们把P值设置的比较小,也就是方向盘打的角度比较小,它的路径是这样的,小车会较晚到达绿线,因为惯性会往返绿线两侧,但是因为方向盘角度小,小车可以在较少的往复次数下接近绿线。

所以我们对比一下设置这三种P值的结果,在小车与绿线相同距离的情况下,P值越大,小车的反应越快,P值越小,小车的反应越慢,所以这三个小车第一次到达绿线的时间是不一样的。虽然P值大能够较快的到达绿线,但是反应比较剧烈,总是因为过快冲过了头。相反P值小的反应比较平缓,但是它反应太慢,我们有时候接受不了。

什么样的P值是合理的呢,就是设置后,小车的反应不是很剧烈,反应时间你也能够接受,那这就是一个相对合理的P值。

那有没有一种办法让它反应再快一点,反应又不那么剧烈呢,那就要用到接下来我们要讲的微分控制了。

 

 

10分钟了解无人机飞控3大算法

我们为了不让这个小车冲过头,我们再给它加一个力,这个力就是D,让这个力来起一个作用,就是让小车越接近绿线的时候,接近绿线的速度越慢,小车越远离绿线的时候,接近绿线的速度相对较快,这个接近绿线的速度不是小车前进的速度,是小车与绿线平行线之间的相对速度。这个D大家可以理解为小车靠近绿线的一个阻力。

 

 

10分钟了解无人机飞控3大算法

假设我们设置了一个相对合理的P值,在P值不变的情况下,微分控制中D值的变化会有怎样的结果。比如我们设置了一个比较合适的D值,微分控制(D)让小车在靠近绿线时,接近绿线的速度比较慢,这样比例控制(P)就可以很轻松的让小车到达绿线上方行驶。

 

 

10分钟了解无人机飞控3大算法

如果D值过大,也就是小车靠近绿线的阻力过大,这样会让小车需要比较长的时间才能到达绿线上方。

 

 

10分钟了解无人机飞控3大算法

如果D值设置的过小,也就是小车靠近绿线的阻力过小,那微分控制(D)就不会对比例控制产生大的影响,所以虽然小车能够较快到达绿线,但小车需要多调整几次,在绿线的两侧往复几次后才能到达绿线上方行驶。

那这样看来比例控制(P)和微分控制(D)的配合,貌似已经很完美了。为什么还要有积分控制(I)呢?

 

 

10分钟了解无人机飞控3大算法

设置合适的P值和D值,可以让小车很好的沿着绿线一直走,但路上不是很平坦,会有些坑坑包包,或者其他的一些干扰,路况不好就会让小车的行进路线发生偏移,比如小车在这里遇到了坑坑包包,它的行进路线 就会变成这样,稍微偏离了绿线一点,因为微分控制(D)让小车离绿线越近时,靠近绿线的速度越慢,比例控制(P)让小车在接近绿线时,方向盘又打的比较小,所以小车要走一段路才回到绿线上面。

有没有办法让它更快回到绿线上面呢,所以我们再给它加一个力,这个力就是I,积分控制。我们让积分控制起这样的作用:如果PD的调节不是很理想的话,就让I帮他俩一把,向P的方向上加一个力,这样可以让小车更快回到目的路径。

设置了I以后,I会根据误差和误差经历的时间进行积分,然后决定施加给目标方向的力的大小,就能够让小车回到目标轨迹上。

 

 

10分钟了解无人机飞控3大算法

一个合适的I值,可以让小车偏离轨迹后,I可以在合适的时间给P一个合适的力,让小车快速的回到绿线上面;

 

 

10分钟了解无人机飞控3大算法

如果I值过大,积分控制(I)调整的力就会比较大,它在帮P的时候会用力过猛,会让小车冲过绿线,下次帮忙的时候还是用力过猛,P表示很无奈,毕竟I也是好心,经过几次调整后,小车终于回到绿线上面,但我们看小车的轨迹线产生了一定的振荡;

 

 

10分钟了解无人机飞控3大算法

如果I值过小,积分控制(I)调整的力就会比较小,就像有一个手无缚鸡之力的柔弱小鲜肉一样,帮不上太大的忙,所以小车回到正确轨迹的时间就会比较长。

 

10分钟了解无人机飞控3大算法

上图中指向左侧的箭头写的是过大不是过人

说完了PID控制对小车的影响,下面我们再说说PID控制对多旋翼的影响。比如这架多旋翼想要作的是保持机身的水平平稳。

我们先从P开始,如果P值设置的过大,哪怕机身有那么一丁点倾斜,飞行器都会用稍大一些的力去调整,结果用力过猛,又继续向回调整,这样飞行器就会频繁的调整自己的水平状态,导至机身产生振动。

 

 

10分钟了解无人机飞控3大算法

上图中指向左侧的箭头写的是过大不是过人

如果P值设置的过小,飞行器的水平调整就会显得力不从心,比如空气中的微风让飞行器发生了倾斜,飞行器向回调整的力比较小,所以需要长一点的时间才能调平,这样让我们觉得它反应有点慢。

 

 

10分钟了解无人机飞控3大算法

上图中指向左侧的箭头写的是过大不是过人

比如我们经过多次测试设置了一个比较合适的P值,可以让飞行器有一个我们能接受的反应时间,但稍有一点过冲,会有一些震荡,接下来再设置D值,让D值消除震荡,如果D值设置过大,会让飞行器恢复平衡时间过长,反应变慢,

 

 

10分钟了解无人机飞控3大算法

上图中指向左侧的箭头写的是过大不是过人

如果设置了一个过小的D值,会导致效果不明显,飞行器还是会有震荡。

 

 

10分钟了解无人机飞控3大算法

上图中指向左侧的箭头写的是过大不是过人

比如我们又设置了一个合适的D值,可以让飞行器反应不是很慢,也不会有明显的震荡。接下来我们再设置一下I值。

这里的I我们可以这么理解,I根据飞行器的反应时间,适当的帮忙,如果飞行器反应慢了,他就会帮一下,能让它的反应快一点。但是如果值设置的过大,这个帮忙可能会用力过猛,导致过冲。

 

 

10分钟了解无人机飞控3大算法

上图中指向左侧的箭头定的是过大不是过人

如果I值设置过小,它的帮忙就没有太大的作用,还是不会让飞行器反应更快。所以设置一个合适的I值需要多次的测试,让它的帮忙起作用,又不会用力过猛。

有些人怕麻烦就不设置I值了,其实也没有太大的影响,只不过离完美还差那么一点。

PID的设置是一个非常复杂的过程,对于一般的使用者来说是非常难的。所以有些厂商也想出了一些办法。

 

 

10分钟了解无人机飞控3大算法

比如3DR的开源飞控APMPIXHWAK,咱们看它的调参软件中,PID部分有这么多要调的地方,对于一般的用户来说是比较有难度的,所以它加了一个自动调参的功能,就是这个AutoTune[‘ɔ:toʊtən],可以把遥控器上的一个开关设置成自动调参,找一片开阔地让飞行器起飞,然后把遥控器上的自动调参开关打开,飞控就开始自己控制飞行器进行飞行测试,然后根据测试情况自动设置一个比较合适的PID参数。

 

 

10分钟了解无人机飞控3大算法

有些厂商作的就更简单了,直接在调参软件里加了一个感度的配置,这个感度大家可以理解为敏感度 ,数值越大,敏感度越高,不同的轴距都有不同的推荐感度设置。

左面的这个是大疆NAZA飞控的调参软件,调参软件说明书里就有一个不同轴距的推荐感度值,右面的拓攻更简单,直接选择轴距,就能自动设置推荐的感度值。

这些厂商的调参软件里一般都会有姿态感度和基本感度,或者叫稳定感度。姿态感度是指飞行器对遥控指令的敏感程度,基本感度或稳定感度是指飞行器对于外界干扰反应的敏感程度。这样就比PID理解起来要简单的多了。

少儿编程Scratch第14讲:链表和冒泡排序算法

前言:

SAP/ORACLE/Salesforce/MS Dynamic这些企业应用软件多以SQL数据表为基础,加上应用平台提供了很多类库,程序员进行底层算法设计的机会可能并不多。但从计算机技能学习来说,自己动手编写和实现一些算法还是必要的。至少,笔试、面试,算法还是会被考到的。

 

目的:

使用冒泡排序算法,对数组中的数值由大到小进行排序。通过比较冒泡,了解冒泡排序的原理。初步了解数组链表功能,往链表中添加数据,读取链表的数据,删除链表数据,获取链表数据条目数量。

 

适用对象:适合有一定初步Scratch编程经验的用户,例如一二年级学生。本算法单独完成的难度比较大,需要家长讲解。

 

本游戏共享在:https://scratch.mit.edu/projects/213542724/

 

微信订阅号:ABO_CRM

实现方式

冒泡排序制作思路:

1)        背景图:既然是冒泡,那就加个海底的冒泡的背景图。

2)        火龙角色:为了增加趣味性加入了一个火龙角色,开始时清空数据,然后随机填充15个数字,接着进行排序。火龙负责说话,提示当前系统在做什么。

3)        数组(链表)变量一个:Data

4)        设置三个变量:循环次数,当前位置,临时变量。

 

少儿编程Scratch第14讲:链表和冒泡排序算法

变量和链表。

5)        清空数据:程序开始时,清空链表中的数据。

a.     火龙提示“开始清空数据。”

b.     使用链表删除指令,可以删除链表指定位置条数条目,也可以全部删除或者删除尾部的数据。因此如果简单,可以直接全部删除。不过为了体现操作过程,我们使用了重复执行,读取条目总数,如果条目总数>1就删除第一条,然后等待0.1秒。这样就能看见系统依次删除链表中的数据。

c.     火龙提示,“数据已清空”。

少儿编程Scratch第14讲:链表和冒泡排序算法

清空,填充数据。

6)        随机填充数据:

a.     火龙报“开始随机填充数据”

b.     强制加了一条最小的数0到链表;

c.     然后执行14次,1-999中间随机选择一个数字,填充到链表中。填充中间未等待,因此一会填充好了15个数字。

 

少儿编程Scratch第14讲:链表和冒泡排序算法

(初始化后开始冒泡)

7)        冒泡排序:大循环,逐个比较。

a.     设置当前位置变量为15,从最后一个开始,和前面一个数相比较,如果比前面的数据大,就要往上冒泡,否则就不冒泡。

b.     设置循环次数:为当前位置减1,即第一次大循环,总最下面的第15个数字开始,一直往上比较,比较14次。1514比,1413比,最后是21比较。第一次将15个数字中,最大的数据冒到最上面,即位置1。第一次大循环结束。

c.     因为第一个数字已经是最大数,因此只要对2-15的数字进行第二轮排序即可。这样第二个大循环,重复比较的循环次数就可以减少1。因此第二次比较了13次,1514比,1413比,最后是32比较。即14个数字中找最大。第二次大循环结束。

少儿编程Scratch第14讲:链表和冒泡排序算法

大循环。

d.     依次类推,每次都找一个最大的,搬到最上面去。

e.     PP-1位置的数字相比较时,如果P>P-1,即下面的数字大,就要交换。因此先将P-1存在临时变量里面;然后P挪到P-1,然后临时变量挪回P,实现P往上冒一格。火龙报“需要冒泡”或者“不需要冒泡”。

f.     冒泡比较后,等待0.5秒,这样能够看见在比较,冒泡,交换数字。

g.     每个大循环之间等待2秒,让火龙报“下一个大循环开始。”

 

少儿编程Scratch第14讲:链表和冒泡排序算法

排序结束

原文始发于微信公众号( 艾博特 ):少儿编程Scratch第14讲:链表和冒泡排序算法|

如何成为一名机器人工程师(二)

来自《机器人工程师学习计划》,原文是大疆工程师YY硕对自己过去学习方式的一个总结。

上周,给大家介绍了想成为一个机器人工程师,大一、大二的学生该怎么做。本周继续为你介绍大三和大四该做什么准备…

【新春特辑】如何成为一名机器人工程师(二)

大三一定要培养出自己一个人独立造出一个完整机器人的能力,比如一个Robocon水平的机器人,或者一个RoboMasters的战车,或者一个四旋翼飞行器。

【新春特辑】如何成为一名机器人工程师(二)

还记不记得小瓦君上周的文章?里面大二应该掌握的知识在这里就一一体现了哦!

【新春特辑】如何成为一名机器人工程师(二)

四旋翼飞行器的机械部分最简单,但是算法比较复杂。对于大三学生来说,从零开始一步到位写一个稳定的飞控比较困难,因为飞控里面有很多细小的知识点要注意。

 

学习四旋翼飞行器
的几个步骤

 

1
自己调一个小四轴飞起来

 

从学习的角度来说,笔者比较推荐的是淘宝店:第七实验室卖的STM32F405飞控,买回来以后自己再随便买个机架(比如大疆F450)、接收机和遥控,就能按照飞控板附带的学习资料、调试软件飞起来。

 

2
看硬件图、读代码

chiplab7的飞控板附带一大堆学习资料,对加速度计、陀螺和磁感计都有很仔细的解释,硬件链路图也很详细。发现代码有bug和看不懂的地方,都可以直接找掌柜问哦~

 

看完代码以后,对一个飞控系统的基本模块:姿态解算、控制解算、混控输出、遥控器处理、嵌入式处理就很明白了。然而这里面有很多技术是需要另外学习的。

 

除了基本的嵌入式编程以外,还有要把大二大三学的信号处理方面的知识再捡起来看看。因为飞行器在空中有振动,会让加速度计产生噪声,为了把这些噪声去除掉,需要对加速度计给出的信号做低通滤波处理,如何选择滤波器的参数呢?如果滤波滤得太狠,延迟就会比较大,对控制的表现会有影响;如果滤波滤得不够,可能会有一些低频的噪声偶尔会出现,导致加速度计的观测不能用。

 

另外最重要的是要理解姿态解算控制解算这两块知识。chiplab7的飞控板的代码采用的是最简单的互补滤波算法做为姿态解算模块,然后控制解算是对欧拉角的三个角度做闭环PID控制,基本都是基础的基础了。

 

3
小修小改加深理解

chiplab7的飞控是靠气压计定高的,飞行效果非常奔放。这时候可以淘宝买个20块钱的超声波模块,然后自己写个高度环去稳定飞控的定高表现。

 

这个过程至关重要,因为高度控制相对来说是个比较直观理解PID控制的方式,而且chiplab7的飞控加高度控制非常好加。工作量不大,因为改善效果很显著,所以可以让人很有成就感,加深继续学习的乐趣!

 

4
理解核心的数学和控制知识

 

姿态解算和控制解算涉及的知识有:

 

  1. 刚体姿态的表示、运动学方程和动力学方程。

主要是对牛顿-欧拉方程的认识和理解、刚体姿态的欧拉角表示法、姿态与角速度的关系等等。可从维基百科入手

 

  2. 自动控制原理。讲PID的书和文章就有很多~

 

  3. 线性估计基本原理。其实就是互补滤波:Reading a IMU Without Kalman: The Complementary Filter 

 

5
重头开始造轮子

 

知乎著名网友vczh曾经说过,学习要抱着勇于造轮子的心态才能进步。所以在熟悉了别人飞控基础上,可以自己重头造一个飞控的轮子。

 

可以自己从芯片开始重新画一个飞控板,读读STM32的芯片手册、读读各种传感器的芯片手册,自己手画一个飞控的原理图、做PCB layout、制板自己焊元件,全套花不了1000块钱,能够加深很多对硬件的理解。

 

 

重视积累

制造整个机器人的过程中要特别重视文档的积累。
 

1

 

【新春特辑】如何成为一名机器人工程师(二)

 

2

【新春特辑】如何成为一名机器人工程师(二)

【新春特辑】如何成为一名机器人工程师(二)

如果你一直往里面积累自己工作中的记录,等你将来工作了,这个文件可能会值很多钱。

 

3

另外特别重要的一点是机器人系统里的嵌入式平台都有烧坏的可能性,有可能在某个嵌入式Linux平台上面辛辛苦苦写了一个多月代码,这个平台突然烧坏了,代码也就丢了。因此你的机器人如果有嵌入式Linux系统在里面,一定要尽早顶起路由器,代码定时提交SVN或者git。

笔者建议

有机会,一定要参加至少一届机器人比赛。那是一件磨练心性的事情。你可能体会到和胜利擦肩而过的痛苦,体会到没机会再来一年的遗憾。参加机器人比赛,很努力,然后失败了,是一件让人快速成长的事情。

 

大三的暑假要思考自己大四应该干什么。不管是出国还是保研,最重要的目的是给自己争取到未来2-3年能够在一个优秀的环境中安心提升自己,有比较好的学习资源,能够参与到一些不错的项目中去。

另外你还需要选定自己将来的细分研究方向,也就是在上周文章开始提到的感知、认知、行为几个方向,而且开始往这个方向深挖。

 

出国去学机器人学方面的知识你有很多不错的选择,比如

世界第一的机器人研究院卡耐基梅隆大学

麻省理工学院的CSAIL实验室

北美传统计算机四大名校(麻省理工学院,卡耐基梅隆大学,斯坦福大学,加州大学伯克利分校)

【新春特辑】如何成为一名机器人工程师(二)

大四可以开始读一些著名入门书籍,笔者建议没有必要全部去读,而且每本书先读前几章就够了,能读多少尽量读多少。

 

  1. 概率机器人学

  2. 凸优化

  3. 线性系统理论

  4. Multiple View Geometry in Computer Vision

  5. 线性估计

  6. 《机器学习》,周志华老师的书

  7. An Invitation to 3-D Vision

  8. Modern Control Systems

  9. Rigid Body Dynamics

  10. Feedback Systems: An Introduction for Scientists and Engineers

 

这些书,大部分特别贵,你可以找一些电子版或影印版的而来看~

 

在读上面这些书的时候,matlab,python都要放在手边,然后把书里面的知识尽量实践出来。很多教科书里都会在章节后面的习题里放一些写明是用matlab做的习题,要尽量多做一些这样的题。

 

ROS

你可能早就听说了ROS的大名,但是最好不要在大四之前去碰它。

 

ROS用了很多操作系统和网络的底层技术。它设计目标是把机器人的控制和传感器处理的软件和它的硬件隔离开,用上ROS以后,你可以方便地用到很多能直接跑的软件代码。但是ROS从入门到精通需要至少一年以上的时间,你必须不断地用,不断地尝试新的代码和硬件,才能对它熟悉起来。

重视实习和毕业设计

要把做毕业设计的过程看做一个正式的项目。这个项目除了做好技术方面的工作,也要做好展示方面的工作。

 

通过PPT介绍、展示自己的成果在工程师的职业生涯的任何一个阶段都非常重要,它甚至也一定程度上限制了机器人工程师能够达到的高度。只有能够把自己的成果清晰地表达给自己的团队,才能获得其他人的反馈、通过沟通提高团队的整体凝聚力和知识水平,这样自己在团队能够获得更多的认可,有助于团队整体工作效率的提高。

【新春特辑】如何成为一名机器人工程师(二)

为了写出美观的技术报告和毕业论文,你可以开始学习Latex

 

【新春特辑】如何成为一名机器人工程师(二)

写毕业论文有个问题是怎么做出精美的矢量图,我推荐Draw Freely | Inkscape,一个比Illustrator更轻量化、但是有些功能反而更强大的免费软件。

 

对于那些想申请出国留学的同学,你要做一个自己个人的成果展示,用网页的形式呈现比较好。把自己Github链接(如果你按照上周文章说的,大学第一天就申请Github账号,现在已经是一个三年的老油条了)、做过的机器人视频、写过的技术报告和文章(最好是英文的)放在上面。

大四到研究生之前的暑假,最好去一些比较不错的机器人公司实习一下,或者继续做机器人比赛。

 

第118讲 boost::algorithm::string之其他

字符串算法我们说来也好几讲的内容来,剩下的已经不多又或者是我们没有必要再一一例举,那么这一讲我们就来结束关于boost::algorithm::string剩下的一些内容。


判断一个字符属于什么类型这些小工具boost::algorithm::string自然也是有提供来的,比如:

//================================================

is_space : //字符是空格或者是制表符

is_alnum : //字符是字母或者数字字符

is_alpha : //字符是否为字母

is_cntrl : //字符是否为控制字符

is_digit : //字符是否为十进制数字

is_xdigit : //字符是否为十六进制数字

is_graph : //字符是否为图形字符

is_lower : //字符是否为小写字符

is_upper : //字符是否为大写字母

is_print : //字符是否为可打印字符

is_any_of : //字符是否为参数中的任意字符

is_from_rang : //字符是否在指定范围之内


//===============================================


除来这些分类函数之外,自然也和我们说过的MString一样拥有start_with ,end_with,他们同样有i开头的版本,i开头的自然是不区分大小写的比较,同样也有修剪系列函数,他们的功能和我们说过的MString的修剪函数一样,而且名字也一样,trim_left,trim_right,trim,他们也有带有_copy后最的版本,但是boost里面的却还有_if的版本,他们需要一个函数对象或者能够作为判断式的函数指针,我们甚至可以认为非_if的版本是_if后缀版本的特殊情况。


除来上面这些还有一些有用的函数,比如contains,该函数判断一个字符串中是否包含指定字符串,equals判断两个字符串是否相等,lexicographical_compare函数用于根据字典顺序判断第一个字符串是否小于第二个字符串,当然这些函数也拥有i前缀的版本,这些函数可以说在实际开发过程中是相当有用的。


//================================================

if (boost::algorithm::is_digit()(48)){

std::cout <<"

48 is digit"

<<std::endl;

}

else{

std::cout <<"

48 isn't digit"

<<std::endl;

}


if (boost::algorithm::is_xdigit()(75)){

std::cout <<"

75 is xdigit"

<<std::endl;

}

else{

std::cout <<"

75 isn't xdigit"

<<std::endl;

}


if (boost::algorithm::is_cntrl()(13)){

std::cout <<"

13 is cntrl "

<<std::endl;

}

else{

std::cout <<"

13 isn't cntrl "

<<std::endl;

}


if (boost::algorithm::is_print()(13)){

std::cout <<"

13 is print "

<<std::endl;

}

else{

std::cout <<"

13 isn't print "

<<std::endl;

}


if (boost::algorithm::is_alnum()(70)){

std::cout <<"

70 is alnum "

<<std::endl;

}

else{

std::cout <<"

70 isn't alnum "

<<std::endl;

}


if (boost::algorithm::is_any_of("

abcdefg"

)(67)){

std::cout <<"

67 is is_any_of(acbdefg)"

<<std::endl;

}

else{

std::cout <<"

67 isn't is_any_of(acbdefg)"

<<std::endl;

}


if (boost::algorithm::is_from_range(67, 90)(70)){

std::cout <<"

70 is is_from_range(67,90)"

<<std::endl;

}

else{

std::cout <<"

70 isn't is_from_range(67,90)"

<<std::endl;

}


std::string str("

Hello World Hello Hello"

);

assert(boost::algorithm::istarts_with(str, "

hello"

));

//不区分大小写

assert(boost::algorithm::starts_with(str, "

Hello"

));

//区分大小写

assert(boost::algorithm::iends_with(str, "

hello"

));

//不区分大小写

assert(boost::algorithm::ends_with(str, "

Hello"

));

//区分大小写


str = "

Hello "

;

boost::algorithm::trim(str);

// 将前后空格去掉

assert(boost::algorithm::equals(str, "

Hello"

));

assert(boost::algorithm::iequals(str, "

hello"

));


//======================================

第118讲 boost::algorithm::string之其他

这里就列举这些,其他的大家按照上面的方法来试着怎么用吧。


关于字符串的操作的,只要熟练使用来这些就基本足够应付各种问题来,如果要遇到这些都无法满足的情况,那么就开大招——使用正则表达式吧。大家可以预先去了解一下正则表达式的东西,因为我们可能不在课堂上给大家介绍正则表达式怎么写,这里只会告诉大家怎么使用C++中的正则表达式。

//=============================================

回复D或者d查看目录,回复阿拉伯数字查看相应章节


原文始发于微信公众号(

C/C++的编程教室

):第118讲 boost::algorithm::string之其他

|

第117讲 boost::algorithm::string之分割与合并

在boost的字符串算法中,关于分割的函数应该是有2个的,还记得我们在115讲里面说到的find_all吗?从某些角度来说这也算是一种分割方式,只是这比较特殊一些,所有的结果都是一样的而已,那么这一讲我们来说一个一般的分割,其实关于分割大家都比较清楚的了,简单点说就是将一个字符串按照某种方式将他分成N多段,这种对字符串的操作通常很有用,在实际项目中对字符串的处理可以说是随处可见,而分割字符串更是家常便饭了,那么说了这么多废话,我们来点实际的东西。


//==========================================


template<typename SequenceSequenceT, typename RangeT, typename PredicateT >

inline SequenceSequenceT&

split(

SequenceSequenceT&

Result,

RangeT&

Input,

PredicateT Pred,

token_compress_mode_type eCompress=token_compress_off )


//==========================================


这就是split的函数声明,SequenceSequenceT可以是vector,deque,list等标准容器,对Input的要求也没那么多,通常于我们来说只要是string或者是boost::iterator_range<std::string::iterator>即可,当然我们通常都是对string进行处理的,所以这我们就不关心了,PredicateT是一个判断式,可以理解为一个函数对象,因为这里是当以当前字符为参数时返回true的话那么当前点就是一个分割点,我们不用纠结这东西,boost本身就提供有一个已经很完备的判断式了:is_any_of,我们只需要将我们要分割的东西给他就行,看下面的例子就知道。在结果中分割字符串是要被丢弃的。token_compress_mode_type 这个参数的目的简单点说就是这种形式 :: ,这里是当作两个:号来处理呢还是就是:号来处理?嗯,这句话说得不清不楚,还好在程序有时候比语言的表述来更直接一些:


//=========================================

string str("

Hello :::::: World"

);

vector<string>result;

split(result,str,is_any_of("

:"

));

// 使用空格和:来进行分割


// 这里会得到结果是 "

Hello"

"

"

"

"

"

"

"

"

"

"

"

"

"

"

"

World"


result.clear();

split(result, str, is_any_of("

:"

),token_compress_on);

// 使用空格和:进行分割


// result : "

Hellow"

"

World"


//========================================


还是代码来得实在一些,简单两句代码就将一些于我来说很难用语言来描述的现象给解释清楚了,从上面的函数原型里面我们看到split的最后一个参数是有默认值的,那就是token_compress_off,这就是不压缩处理,而我们开启压缩处理后,那些:::::都被当成一个:,有因为空格和:都是连在一起的,而空格也是被定义为分隔符,所以也都被压缩在一起进行处理了。于是乎我们就得到了想要的Hellow World了。


和split相对的是join,顾名思义,这就是将字符串连接起来,所以他的用法很简单,不过他有两个版本,一个带有_if后缀的版本,这自然是需要一个判断式,只有满足判断式的字符串才会被添加到链接队列之中,我们来看看他们的用法:


//==========================================

std::string str("

Hello,World,Ni,Hao,A"

);

std::vector<std::string>v_str;

boost::algorithm::split(v_str, str, boost::algorithm::is_any_of("

,"

),boost::algorithm::token_compress_on);

// 将字符串按照,号进行分割


std::string __str = boost::algorithm::join(v_str, "

—"

);

// 再将字符串重新连接起来

// Hello—World—Ni—Hao–A


std::string __str2 = boost::algorithm::join_if(v_str, "

—"

, [&

](const std::string&

s){


return boost::algorithm::contains(s, "

l"

);

});

// 只连接包含l字符的字符串

// 结果为Hello—World


//=========================================


不论是split还是join,他们都是对字符串进行处理,那么很多时候我们可能想要的不是字符串,有可能是其他的东西,比如是数字等等,为了解决这个问题,我们可以对split和join进行扩展(可以参考我们前面说的MString,他们可以转变为任意类型的东西)。



//========================================

template<typename T,typename A,template<typename T1,typename A1>class C>

void __split(C<T, A>&

result, const std::string&

input,const std::string&

spliter,bool isPress = false){

result.clear();

if (input.empty())

return;

std::vector<std::string>__result;

boost::algorithm::token_compress_mode_type __type;

if (isPress)

__type = boost::algorithm::token_compress_on;

else

__type = boost::algorithm::token_compress_off;

boost::algorithm::split(__result, input, boost::is_any_of(spliter), __type);



for (auto&

t : __result){

try{

T __t = boost::lexical_cast<T>(t);

result.push_back(__t);

}

catch (boost::bad_lexical_cast e){

;

}

}

}


//=================================

// 针对string进行特化

//=================================

template<typename A, template<typename T1, typename A1>class C>

void __split(C<std::string, A>&

result, const std::string&

input, const std::string&

spliter, bool isPress = false){

result.clear();

if (input.empty())

return;

std::vector<std::string>__result;

boost::algorithm::token_compress_mode_type __type;

if (isPress)

__type = boost::algorithm::token_compress_on;

else

__type = boost::algorithm::token_compress_off;

boost::algorithm::split(result, input, boost::is_any_of(spliter), __type);

}

template<class T,class A,template<class T1,class A1>class C>

std::string __join(const C<T, A>&

Input, const std::string&

joiner){

std::vector<std::string>__v_str;


for (auto&

c : Input){

try{

std::string str = boost::lexical_cast<std::string>(c);

__v_str.push_back(str);

}

catch (boost::bad_lexical_cast e){

;

}

}


return boost::join(__v_str, joiner);

}


//==================================================

// 同样需要针对string进行特化

//==================================================

template<class A, template<class T1, class A1>class C>

std::string __join(const C<std::string, A>&

Input, const std::string&

joiner){


return boost::join(Input, joiner);

}


//=========================================================


现在使用起来就变得相对简单了:

//=========================================================

int main(){

std::string str("

hello,World,Ni,Hao,A"

);

std::vector<std::string>v_str;

__split(v_str, str, "

,"

);


std::cout <<"

===============split=============="

<<std::endl;


for (auto &

v : v_str){

std::cout <<v <<std::endl;

}

std::string __str = __join(v_str, "

—"

);


std::cout <<"

================join============="

<<std::endl;

std::cout <<__str <<std::endl;

std::string __str2 = boost::algorithm::join_if(v_str, "

—"

, [&

](const std::string&

s){


return boost::algorithm::contains(s, "

l"

);

});


std::cout <<"

===========join if=================="

<<std::endl;

std::cout <<__str2 <<std::endl;


str = "

Hello :::::: World"

;

v_str.clear();

boost::algorithm::split(v_str, str, boost::algorithm::is_any_of("

:"

),boost::algorithm::token_compress_on);


str = ("

123,345,981,3345,98"

);

std::vector<int>v_int;

__split(v_int, str, "

,"

);


std::cout <<"

=========split for int============"

<<std::endl;


for (auto&

v : v_int){

std::cout <<v <<std::endl;

}


std::string str3 = __join(v_int, "

—"

);

std::cout <<"

========join for int============"

<<std::endl;

std::cout <<str3 <<std::endl;


return 0;

}

//=====================================================

第117讲  boost::algorithm::string之分割与合并


关于join_if的扩展就留给大家当是练习吧。


转眼间,我们的编程教室都已经两年了,只是今年好像都一直比较忙,所以更新得不多,但是会坚持下去,谢谢大家的一直支持,新的一年里,大家共勉。


//===================================================

回复D或者d查看目录,回复阿拉伯数字查看相应章节。


原文始发于微信公众号(

C/C++的编程教室

):第117讲 boost::algorithm::string之分割与合并

|

第116讲 boost::algorithm::string之替换和删除

boost::algorithm::string可以说是很全面的处理字符串的一些算法的集合,上一讲我们说来查找一簇函数,那么这一讲我们说的是替换和删除,为什么替换和删除要一起说呢?因为他们的其实是一样的,删除可以是替换的特殊情况(将其替换为"

"

)。

和查找一簇函数一样,替换和删除函一样拥有多个版本,如下:


//==============================================

replace/erase_first // 替换/删除第一次出现的字符串

replace/erase_last // 替换/删除最后一次出现的字符串

replace/erase_nth
// 替换/删除第n次出现的字符串 ,这个函数和我们说过的MString的有些区别,MString的索引是从开始的,这里是从开始的


replace/erase_all
// 替换/删除所有

replace/erase_head // 替换/删除前面的多少个字节

replace/erese_tail // 替换/删除最后多少个字节


//==============================================


这些函数中前面8个拥有前缀i_版本和后最_copy的版本,前缀i_的版本是不区分大小写的替换或者删除,而后缀_copy的版本是不修改原始字符而返回一个新的你所需要的字符串。而对于后面4个函数来说因为是不管字符问题而是直接修改多少个字节问题所以并没有前缀i_的版本。

下面我们来每个函数简单举个例子给大家演示一下用法,其实相当简单:

//==============================================

std::string str("

Hello World Hello Hello"

);

boost::algorithm::replace_first(str, "

Hello"

, "

NoHello"

);

// 将第一次出现的Hello替换为NoHello

std::cout <<str <<std::endl;

// NoHello World Hello Hello


boost::algorithm::erase_head(str, 2);

// 将前面的两个字节删掉

std::cout <<str <<std::endl;

// Hello World Hello Hello


boost::algorithm::replace_nth(str, "

Hello"

, 1, "

NoHello"

);

// 将第二个Hello替换为NoHello

std::cout <<str <<std::endl;

// Hello World NoHello Hello


boost::algorithm::replace_first(str, "

NoHello"

, "

Hello"

);

// 恢复原状

std::cout <<str <<std::endl;


boost::algorithm::replace_last(str, "

Hello"

, "

NoHello"

);

std::cout <<str <<std::endl;

// Hello World Hello NoHello


std::string str2 = boost::algorithm::replace_last_copy(str, "

NoHello"

, "

Hello"

);

std::cout <<str2 <<std::endl;

// Hello World Hello Hello

std::cout <<str <<std::endl;

// Hello World Hello NoHello

//=================================================

第116讲  boost::algorithm::string之替换和删除

//=============================================

回复D或者d查看目录,回复阿拉伯数字查看相应章节


原文始发于微信公众号(

C/C++的编程教室

):第116讲 boost::algorithm::string之替换和删除

|

第114讲 MString

本打算说boost的string_algorithm的,因为这个库是专门针对字符串处理的,而且原本的计划是说完这个之后我们就继续说正则表达式,但是细想了下,正好手头有这么一个库,他不但有boost的string_algorithm的常用操作方法,同时还提供对正则表达式的操作,还可以和标准库的算法无缝结合,所以,论实用性,该库拥有良好的操作界面,但论效率,自然是比不上标准库的string,主要体现在连续赋值的操作上,这个库原本打算使用标准库的分配器来进行内存分配的,但是后来没有采用,只是进行了char*的封装,测试过malloc和new的性能,结果最后的结果竟然是差不多,所以最后也连malloc和realloc也抛弃了,选择使用new来作为内存的分配器,这样对C++的同学来说如果要看源码也是很好理解的。


该库的下载链接如下:

http://yun.baidu.com/share/link?shareid=111953120&

uk=2114708594


同样是在vs2013下面编译的静态库,所以如果你们要使用的话须得在该平台下,当然如果你们想要源码自己编译自己的平台的话,那么我可以给你们源码。


好吧,我们就跑步进入主题吧,现在先来介绍该库的一些特征。


关于该库的界面,大家可以通过MString.h文件查看所有的接口,不过因为该库使用了模板的结合,又添加有大家的特化(特化是为了更好的提高效率),所以看来可能会比较辛苦,当然好处是可以看见源码。


该库最大的特点:

引用计数,内存共享,使用了完美的写时复制技术,在使用过程可以获得更好的性能体验和内存的控制,因为拥有更大的灵活性同时降低了封装性,当然,有得就会有失。


现在我们来看看怎么使用他:


1,关于构造

和std::string一样简单粗暴的构造方式,他还可以使用一个系列进行构造。


//=====================================

mj::MString str((std::istreambuf_iterator<char>(std::cin)),

std::istreambuf_iterator<char>());

std::cout <<str <<std::endl;


//=====================================

第114讲 MString

该操作将会由键盘输入来构造,遇到Ctrl+Z后停止输入,另一个另类的构建方式:


//=====================================

mj::MString str((std::istream_iterator<char>(std::cin)),

std::istream_iterator<char>());

std::cout <<str <<std::endl;


//=====================================

该构造方式和上面的一样,只是这一次的结果将所有空格都忽略了。

第114讲 MString

还可以通过std::string 的迭代器来构造:


//=====================================

std::string stdstr("

Hello World"

);

mj::MString str(stdstr.begin(),stdstr.begin()+5);

std::cout <<str <<std::endl;


//=====================================

第114讲 MString

所以自然也支持他自己的迭代器构造:


//=====================================

mj::MString stdstr("

Hello World"

);

mj::MString str(stdstr.begin(),stdstr.begin()+5);

std::cout <<str <<std::endl;


//=====================================


其他的都是简单粗暴的构造方式,所以这里就不细说了。


另外的复制构造,赋值操作和你们所想的一样,没啥好说,但是值得一提的是这里的赋值和复制是共享一块内存,所以这里的复制和赋值操作的代价是一个时间常量。


//====================================

mj::MString stdstr("

Hello World"

);

mj::MString str(stdstr);

mj::MString str1;

str1 = stdstr;

std::cout <<stdstr.GetAddr() <<std::endl;

std::cout <<str.GetAddr() <<std::endl;

std::cout <<str1.GetAddr() <<std::endl;

std::cout <<str.reference_count() <<std::endl;


//======================================

这里我们可以看到3个对象地址是一样的,同时也看到当前的引用计数为3

第114讲 MString

下面我们来对任意一个进行修改:


//=====================================

str[0] = 'W';

std::cout <<stdstr.GetAddr() <<std::endl;

std::cout <<str.GetAddr() <<std::endl;

std::cout <<str1.GetAddr() <<std::endl;

std::cout <<str.reference_count() <<std::endl;


//======================================

第114讲 MString

很明显,str的地址改了,而且当前他的引用计数变成了1,但是当我们换个操作方式:


//=====================================

char ch = str[0];

std::cout <<stdstr.GetAddr() <<std::endl;

std::cout <<str.GetAddr() <<std::endl;

std::cout <<str1.GetAddr() <<std::endl;

std::cout <<str.reference_count() <<std::endl;


//=====================================

第114讲 MString

这是并没有发生任何变化,这里也就是说,当我们对MString操作[]作为左值时当前的对象会被单独拿出来进行修改,但是当我们只是简单的读取时并没有发生任何变化,这就是完美的写时复制技巧,可以避免我们在操作字符串时不必要的拷贝和意外的修改。


那么接下来看看他的新特性:


//=====================================

mj::MString str;

str = str + "

Hello "

+ 123 + "

World "

<<789 <<"

This "

<<10 <<"

is only a test

"

;

std::cout <<str <<std::endl;


//=====================================

第114讲 MString

是不是很方便的操作呢,因为该库提供了operator+=,operator+,operator<<等等很多方便的操作符,所以使用起来相当的方便,因为这些操作都是泛型操作,所以只要可以使用ostream<<操作的类型都可以使用这些操作来变换成MString。但是有一点需要注意,看到上面我们的写法了吗?

我们使用:str = str + ………..;

你们可能在想,为什么不直接使用 str += …………….;

这里有个优先级问题,在C++里面, 右边的优先级总是高于左边,所以如果使用 str += ………的话,那么是后面的先执行这些操作,但是后面的这些类型不一定都可以有这些操作,所以就不会成功,但是我们使用 str = str + ………………的话,那么先计算的是 str+ X ,这里结果返回的是MString的引用,所以他可以继续一直操作下去。但是如果我们使用<<的话就更方便了,我们不用考虑什么,直接str<<…….就好。


提供了任意类型转换到MString,那么也提供了从MString转换到任意类型的方法:


ToOtherType(),名字难听了些,但直截了当,不过他的转换从开始遇到无法转换就停止,比如:

//===================================

mj::MString str0("

123 456 789"

);

int
num = str0.ToOtherType<int>();

std::cout <<num <<std::endl;


//结果
num = 123

//==================================

这里在遇到空格时发现无法转换为int于是转换结束,结果就是123,这种方法的好处是不修改原始数据,但是下面的操作就会对原始数据进行修改:


//==================================

mj::MString str0("

123 456 789"

);

int num;

str0 >>num;

std::cout <<num <<std::endl;

// 123

str0 >>num;

std::cout <<num <<std::endl;

// 456

str0 >>num;

std::cout <<num <<std::endl;

// 789

std::cout <<str0.empty() <<std::endl;

// 1


//==================================

operator>>操作符将MString当成一个数据源,>>不断地将数据输出来,为了很好的实现这个操作符,这里使用模板元技巧,将std::string和MString两种类型进行了特殊处理。


//==================================

mj::MString str0("

123 456 789"

);

std::string num;

str0 >>num;

std::cout <<num <<std::endl;

// "

123 456 789"

std::cout <<str0.empty() <<std::endl;

// 1

//==================================


如果需要输出的是std::string 或者是MString的话,会一下子将数据全部返回来。


下面来看看修剪功能:

//=================================

mj::MString str;

str = str + "

Hello "

+ 123 + "

World "

<<789 <<"

This "

<<10 <<"

is only a test

"

;

mj::MString str1 = str;

std::cout <<str <<1 <<std::endl;

str.trim_right();

// 删除右边的空格

std::cout <<str <<1 <<std::endl;

str.trim_left();

// 删除左边的空格

std::cout <<str <<1 <<std::endl;

str1.trim();

// 该操作等效率上面的两个操作

std::cout <<str1 <<std::endl;

//================================


下面再来看看分割操作,分割操作提供了泛型操作,可以支持带两个模板参数的模板容器,而且需要带有push_back操作的,就标准库而言就有vector,list,deque可以用来当作返回容器,该函数的声明为:


//================================

template<typename T,typename A, template<typename T1, typename A1>class C>

void split(is_any_of any, C<T, A>&

result);


//===============================

is_any_of 需要分割的标志,用法很简单:


//===============================

mj::MString str;

str = str + "

Hello "

+ 123 + "

World "

<<789 <<"

This "

<<10 <<"

is only a test

"

;

std::vector<std::string>vv;

str.split("

t"

, vv);

std::cout <<str <<std::endl;

for (auto&

v : vv){

std::cout <<v <<std::endl;

}

//==============================

第114讲 MString

如同结果显示一样,我们按照给定的空格或者t来将数据分割下来。


还有一个分割方式使用正则表达式来进行分割,使用正则表达式来操作的方法都带有reg_作为前缀,如下:


//===============================

mj::MString str;

str = str + "

Hello "

+ 123 + "

World "

<<789 <<"

This "

<<10 <<"

is only a test

"

;

std::vector<std::string>vv;

str.reg_split("

\s+"

, vv);

std::cout <<str <<std::endl;

for (auto&

v : vv){

std::cout <<v <<std::endl;

}

//================================

结果和上面的是一样的,当然使用正则表达式分割方式我们还可以将数据都消除,如下:


//=================================

str.reg_split("

(\s+)|(\d+)"

, vv);

std::cout <<str <<std::endl;

for (auto&

v : vv){

std::cout <<v <<std::endl;

}

//=================================

这次,我们将所有的数字和空格都消除了,剩下的就是字符。


分割操作在平时使用过程中是使用得最为频繁的了。


关于查找功能,如同名字一样,都是以find_为开头,find_first查找第一次出现的位置,find_last查找最后一次出现的位置,find_nth查找第n次出现的位置,这里需要说的是纠结了很久,最后决定让索引从1开始,这些函数都是泛型函数,为的就是更方便的操作,比如:


//==================================

mj::MString str;

str = str + "

Hello "

+ 123 + "

World "

<<789 <<"

This "

<<10 <<"

is only a test "

;

size_t pos = str.find_first(123);

// 我们查找的是123.而不是"

123"

if(pos != mj::MString::npos){

std::cout<<pos<<std::endl;

}

else{

std::cout<<123<<"

isn't exist"

<<std::endl;

}

//==================================

该库的查找核心是KMP算法,其实本来大可以使用标准库的serch或者find等等更为高效的算法,虽然说KMP算法也算是很高效的查找方式,但是终究是自己手写的,没有标准库的那些待优化的做得好,但是该库的目的只是为了操作方便而已,但是对于现在的硬件来说,这点性能的累赘应该不会成为问题,只是如果我们选择使用std::serch的话会获得更高的效率,以后有时候这个会更新。


提供有一系列的替换函数簇,一如名字一样,可以很好的理解:


//==================================

mj::MString str;

str = str + "

Hello "

+ 123 + "

World "

<<789 <<"

This "

<<10 <<"

is only a test "

;

str.replace_first("

123"

, 789);

std::cout <<str <<std::endl;


//=================================

还有一个更为强大系列,使用正则表达式来替换:

//=================================

str.reg_replace("

(\s+)|(\d+)"

, "

"

);

std::cout <<str <<std::endl;

//==================================

该方法将数字和空格全部替换掉。


关于替换一簇函数还有一个带_copy后缀的,使用该系列不会修改原始数据,而且返回一个新的MString对象。


append,push_back等等这些泛型函数也就顾名思义了,size和length这些函数都是时间常量复杂度的。


start_with;

以什么开头

end_with ;

以什么结尾

istart_with;

不区分大小写的start_with

iend_with;

不区分大小写的end_with


erase_系列函数和replace_系列对应,也有带有_copy的系列。


all,判断是否全部满足条件,比如:


//==================================

mj::MString str("

Hello"

);

std::cout<<str.all([&

](char c){


return c >80;

}) <<std::endl;


//==================================


结果为false,因为H<80


to_upper;

全部转换为大写。

to_lower;

全部转换为小写。他们同样有_copy的版本。


下面来看看正则表达式的一些操作:


reg_match 检查字符串是否匹配给定的表达式,必须全匹配才得算正确:

//==================================

mj::MString str("

Hello 123"

);

std::cout <<str.reg_match("

\w+"

) <<std::endl;

// false

std::cout <<str.reg_match("

\w+\s+\d+"

) <<std::endl;

// true

//==================================

reg_serch 只要有一个匹配就返回true

//==================================

mj::MString str("

Hello 123"

);

std::cout <<str.reg_serch("

\w+"

) <<std::endl;

// true

std::cout <<str.reg_serch("

\w+\s+\d+"

) <<std::endl;

// true

//==================================


有时候我们不光只是检查是否有匹配项,还需要获取匹配的结果,下面的两个函数就是满足这个功能的:

//=================================

template<class T,class A,template<class T1,class A1>class C>

bool reg_match(const std::string&

reg, C<T, A>&

result);

// 要求全匹配


template<class T,class A,template<class T1,class A1>class C>

bool reg_serch(const std::string&

reg, C<T, A>&

result) // 只返回第一个匹配的信息


template<class T, class A, template<class T1, class A1>class C>

void reg_serch_all(const std::string&

reg, C<T, A>&

result) // 返回所有匹配的信息

//===================================


看下面例子:

//===================================

mj::MString str("

Hello 123 World 789 hah 102 "

);

std::vector<std::string>vv;

str.reg_serch("

\d+"

, vv);

for (auto v : vv){

std::cout <<v <<std::endl;

} // 结果只有 123


str.reg_serch_all("

\d+"

, vv);

for (auto v : vv){

std::cout <<v <<std::endl;

} // 结果只有 123 789 102


//==================================


copy功能,为什么有这个功能呢,因为该库的特点就是内存共享,所以我们使用普通的复制赋值操作是不能完成有时候我们想要的copy的,所以特点提供有copy功能,该功能将数据重新拷贝一份出来,如果不想让他共享的,可以使用markunshare函数标记一下,该对象的内存就不再被共享。


//==================================

mj::MString str("

Hello 123 World 789 hah 102 "

);

mj::MString str1 = str.copy(0, 4);

std::cout <<str1 <<std::endl;

// Hello

mj::MString str2 = str.copy(str.begin(), str.begin() + 5);

std::cout <<str2 <<std::endl;

// Hello

//==================================

reference_count() 查看当前数据被引用的次数。


如同std::string一样,可以有迭代器操作,begin()和end()的操作方式一样,因为他们,所以MString可以和标准库的算法实现完美的结合,例如:


//=================================

mj::MString str("

Hello 123 World 789 hah 102 "

);

auto it = std::find(str.begin(), str.end(), '1');

if (it != str.end()){

std::cout <<*it <<std::endl;

}

else{

std::cout <<"

find fail …. "

<<std::endl;

}

// 输出 1


std::iter_swap(str.begin(), str.begin() + 4);

std::cout <<str <<std::endl;

// oellH …….


//=================================


最后说一下带类型安全检查的格式化功能,我没有提供其他的函数,而是之间简单的重载operator(),但是通常如果使用格式化的时候最好先使用clear,否则会将数据添加在后面,如下:


//=================================

mj::MString str("

Hello"

);

str("

%1 %2 %3 %4"

, 123, "

world"

, 789, "

Haha"

);

std::cout <<str <<std::endl;

str.clear();

str("

%1 %2 %3 %4"

,"

Hello"

, 123, "

world"

, 789);

std::cout <<str <<std::endl;

//=================================

第114讲 MString

使用起来非常方便,大家可以慢慢探索,如果还有不能满足需求的,可以提出你们的意见。这一讲的内容好像有些多得过分了,不过没关系,大家可以慢慢消化,其实也不多,就像平时使用一样使用就好。


//===============================

回复D或者d查看目录,回复数字查看相应的章节


原文始发于微信公众号(

C/C++的编程教室

):第114讲 MString

|

第七十五讲 细说map(1)

这些天都比较忙,所以没时间更新新内容,不过说实在的,对于C++的内容我们已经没多少可以拿出来说的了,当然对于基础知识,我们早就说完了,有些朋友,没有看过一讲,就问,有没有不要基础的?我想可能大概是我们的章节名都取得太随意太简单太直观化了些,所以有些朋友认为我们的内容说得很基础,但是我想真正看过里面内容的朋友肯定不会这么认为,我们里面引入的都是些很有技巧性的技术,好了,废话不多说,我们今天继续来说新的东西——关联容器。

关于容器,我们说了序列容器,至于大家吸收了多少这就是看大家的了,如果还有不明白的,可以回去多看几遍,其实,STL里面的东西不难如果我们只想去使用的话,尤其是在我们知道他们的原型之后,用起来更是轻松加愉快,当然,如果我们真心要研究STL的算法的话,那可能就有点难度了,所以,我只给大家提供原型和一些简单的实现,想要深入研究STL里面的东西,等我们打算去研究算法的时候再来研究吧。我们现在要做的就是拿来使用。

map在关联容器里的重要性如同vector在序列容器的重要性一样,所以,在说关联容器的时候我们肯定先来说说map。

map第一次接触他的时候我就想怎么就出了个地图了呢?后来又听说他叫字典,我就纳闷,怎么地图就变成了字典了呢?这个笑话有些冷,不过当初真是这样的。好了,我们今天先来看看map吧。
——————————–
template<typename Key,typename T,typename Cmp=less<Key>,

typename A=allocator<pair<const Key,T
>>>
class map{
public:

//还是老规矩,每个容器在一开始都要有一系列的类型定义

typedef Key key_value;



typedef T mapped_type;



typedef pair<const Key,T>value_type;

//这点和我们以前所说的序列容器有所不同,这也就是他的重点。

typedef Cmp key_compare;



typedef A allocator_type;



typedef typename A::reference reference;



typedef typename A::const_reference const_reference;



typedef implementation_defined1 iterator;



typedef implementation_defined2 const_iterator;



typedef typename A::size_type size_type;



typedef typename A::difference_type difference_type;



typedef std::reverse_iterator<iterator>reverse_iterator;



typedef std::reverse_iterator<const_iterator>const_reverse_iterator;



//

//

};


———————————

好吧,今天算是个开始,我们就先熟悉这一些的typedef,这些东西都没啥难的,不过我们要注意一点,那就是上面我们提到的value_type,这里他既不是T也不是Key,而是一个pair<Key,T>,在pair里面,Key是键,T是值,在pair里面,有两个成员,一个first,一个second,我们来看看pair是啥东西就知道了,还记得我们说的快速排序吗?当时我把微软的源码拿出来,里面就用到了pair。
————————–
template<typename Key,Typename T>
class pair{
public:

Key first;



T second;



pair():first(Key()),second(T()){ }

pair(const Key&

x,const T&

y):first(x),second(y){ }

template<typename U,typename V>

pair(const pair<U,V>&

p):first(p.first),second(p.second){ }
};


—————————

Ok,今天先到这里吧。

==========================

回复D直接查看目录

原文始发于微信公众号(

C/C++的编程教室

):第七十五讲 细说map(1)

|