Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

软件开发文档的编写方法

发表于 2020-01-22 | 更新于 2022-07-07

软件开发文档是软件开发过程的输出产物。软件开发过程的不同阶段将产生不同的软件开发文档。例如:软件需求分析阶段将产生软件需求规格说明书,软件概要设计阶段将产生概要设计说明书,软件详细设计阶段将产生详细设计说明书。按照软件工程的原则,软件开发过程输出这些文档的目的是为了保障软件开发的质量,确保软件项目能够按时完成,并保质保量。下面重点介绍各类软件开发文档的编写方法。

软件过程模型

软件过程模型是软件过程的简化表示。典型的软件过程模型有:瀑布模型、增量式开发模型和面向服用的软件工程模型。以瀑布模型为例,其涉及的开发活动如图1所示。

瀑布模型

图1 瀑布模型

各个开发活动对应产出的软件开发文档主要有:

  1. 可行性研究报告
  2. 项目开发计划
  3. 软件需求说明书
  4. 概要设计说明书
  5. 详细设计说明书
  6. 数据库设计说明书
  7. 数据要求说明书
  8. 测试计划
  9. 测试分析报告
  10. 项目开发总结报告
  11. 操作手册
  12. 用户手册
  13. 开发进度月报

软件开发文档

可行性研究报告

可行性研究报告

图2 可行性研究报告

项目开发计划

项目开发计划

图3 项目开发计划

软件需求说明书

软件需求说明书

图4 软件需求说明书

概要设计说明书

概要设计说明书

图5 概要设计说明书

详细设计说明书

详细设计说明书

图6 详细设计说明书

数据库设计说明书

数据库设计说明书

图7 数据库设计说明书

数据要求说明书

数据要求说明书

图8 数据要求说明书

测试计划

测试计划

图9 测试计划

测试分析报告

测试分析报告

图10 测试分析报告

项目开发总结报告

项目开发总结报告

图11 项目开发总结报告

操作手册

操作手册

图12 操作手册

用户手册

用户手册

图13 用户手册

软件开发文档的使用

软件文档分类

软件文档分类

图14 软件文档分类

软件文档读者

软件文档读者

图15 软件文档读者

软件文档使用

软件文档使用

图16 软件文档使用

参考链接

  1. 软件工程,by wikipedia.
  2. 软件需求,概要设计,详细设计(文档)怎么做,做什么?,by 安东尼_Anthony.
  3. 软件工程文档总结,by BONIC.
  4. 国标:计算机软件文档编制规范,by 宋哥.
  5. 软件测试流程,by HenryZ.Tang.

CPlusPlus不常用语法解析

发表于 2020-01-21

近年来C++发展很快,出现了一些新的语法和特性。熟练掌握这些语法和特性,可提高编写C++代码的效率。下面即简要介绍这些C++语法和特性。

const=0

在类声明中,会出现const=0语法,如下所示:

1
2
3
4
5
class Weapon
{
public:
virtual void attack() const = 0;
};

在此处 =0 说明该类成员函数是一个纯虚函数。而将const放在成员函数之后,表示该成员函数禁止修改该类的数据成员(mutable成员除外)。如果您无意中修改了该类的数据成员,编译器会报告一个错误。

参考链接

  1. 关于virtual:c ++:const = 0的方法原型的代码说明,by 码农家园.
  2. C++构造函数和析构函数的调用顺序,by 靖心.

CPlusPlus单元测试框架Catch入门

发表于 2020-01-15 | 更新于 2022-07-11

最近在编写一个飞行力学的类库,随着类数量的增加,代码越来越复杂,质量越来越难以控制,因此引入单元测试,通过自动化测试以保障代码质量,防止因代码修改引入新的Bug。C++已经有一些成熟的代码测试框架,例如:Google Test, Boost.Test, CppUnit, Cute,等等。通过综合分析和比较,最终选择Catch2测试框架。选择该测试框架的原因是其够轻量级,够简单。

Catch2简介

Catch2是轻量级的C++的多范式测试框架。 它也支持Objective-C(也许还有C)。 它主要作为单个头文件分发,尽管某些扩展可能需要其他头文件。

关键特征

  • 快速且非常容易上手。 只需下载catch.hpp,#include它就可以了。
  • 没有外部依赖性。 只要您可以编译C ++ 11并拥有C ++标准库即可。
  • 将测试用例编写为自注册函数(或方法,如果您愿意的话)。
  • 将测试用例划分为几个部分,每个部分都是独立运行的(消除了对夹具的需求)。
  • 使用BDD样式的“时准时限”部分以及传统的单元测试用例。
  • 仅一个核心声明宏可以进行比较。 使用标准C / C ++运算符进行比较-但是完整的表达式已分解,并且记录了lhs和rhs值。
  • 测试使用自由格式的字符串命名-合法标识符中没有其他名称。

核心特征

  • 可以对测试进行标记,以方便地运行临时的测试组。
  • 故障可能(可选)进入Windows和Mac上的调试器。
  • 输出通过模块化报告器对象。 包括基本的文本和XML报告程序。 自定义记者可以轻松添加。
  • 支持JUnit xml输出以与第三方工具(例如CI服务器)集成。
  • 提供了默认的main()函数,但您可以提供自己的控件来进行完全控制(例如,集成到自己的测试运行器GUI中)。
  • 提供了命令行解析器,如果您选择提供自己的main()函数,该解析器仍可以使用。
  • Catch可以测试自己。
  • 替代性断言宏报告失败,但不中止测试用例
  • 浮点公差比较是使用表达性的Approx()语法构建的。
  • 内部和友好的宏是隔离的,因此可以管理名称冲突
  • 匹配器

Catch示例

使用Catch进行单元测试,只需简单掌握TEST_CASE、REQUIRE、SECTION三个宏即可编写绝大部分的测试用例。简单示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
TEST_CASE( "vectors can be sized and resized", "[vector]" ) {

std::vector<int> v( 5 );

REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );

SECTION( "resizing bigger changes size and capacity" ) {
v.resize( 10 );

REQUIRE( v.size() == 10 );
REQUIRE( v.capacity() >= 10 );
}
SECTION( "resizing smaller changes size but not capacity" ) {
v.resize( 0 );

REQUIRE( v.size() == 0 );
REQUIRE( v.capacity() >= 5 );
}
SECTION( "reserving bigger changes capacity but not size" ) {
v.reserve( 10 );

REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 10 );
}
SECTION( "reserving smaller does not change size or capacity" ) {
v.reserve( 0 );

REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
}
}

上述示例中,对于每个SECTION,TEST_CASE都是从头开始执行的,因此,当我们进入每个部分时,我们知道vector的大小为5,容量至少为5。通过REQUIRE宏在顶层确保vector大小和容量的正确性。这是可行的,因为SECTION宏包含一个if语句,该语句回调Catch来查看是否应执行该节。 通过TEST_CASE,每次运行都会执行一个叶子部分。 其他部分将被跳过。 下次执行下一个部分,依此类推,直到没有新的部分为止。

参考链接

  1. Writing Unit Tests with Catch and CMake,by filebox.
  2. Integrating catch2 with CMake and Jenkins,by mariuselvert.
  3. Catch2,by catchorg.
  4. C++单元测试入门,by pezy.
  5. Catch2 - 用于 test 的轻量级库,by Bluemultipl.

四元数与旋转矩阵

发表于 2020-01-15 | 更新于 2021-04-09

四元数是由爱尔兰数学家威廉·卢云·哈密顿在1843年创立出的数学概念。单位四元数(Unit quaternion)可以用于表示三维空间里的旋转。它与常用的另外两种表示方式(三维正交矩阵和欧拉角)是等价的,但是避免了欧拉角表示法中的万向锁问题。比起三维正交矩阵表示,四元数表示能够更方便地给出旋转的转轴与旋转角。

欧拉角

欧拉角(Euler Angles)是一种描述三维旋转的方式,根据欧拉旋转定理,任何一个旋转都可以用三个旋转的参数来表示。但欧拉角的描述方式有很多种,并没有一个统一标准。对于定义一个欧拉角,需要明确的内容包括:

  1. 三个旋转角的组合方式(是xyz还是yzx还是zxy)
  2. 旋转角度的参考坐标系统(旋转是相对于固定的坐标系还是相对于自身的坐标系)
  3. 使用旋转角度是左手系还是右手系
  4. 三个旋转角的记法

旋转角的记法

顺序 飞行器 望远镜 符号 角速度
第一 heading azimuth $θ$ yaw
第二 attitude elevation $ϕ$ pitch
第三 bank tilt $ψ$ roll

旋转矩阵

对于两个三维点 $p_1(x_1, y_1, z_1)$,$p_2(x_2,y_2,z_2)$,由点 $p_1$ 经过旋转矩阵 $R$ 旋转到 $p_2$,则有:

$$R = \left[ \begin{matrix}
r_{11} & r_{12} & r_{13}\
r_{21} & r_{22} & r_{23}\
r_{31} & r_{32} & r_{33}
\end{matrix} \right]$$

$$\left[ \begin{matrix}
x_2 \
y_2 \
z_2
\end{matrix} \right] = R
\left[ \begin{matrix}
x_1 \
y_1 \
z_1
\end{matrix} \right]$$

注:旋转矩阵为正交矩阵$RR^T=E$

  • 绕x轴旋转:

$$R_x(\theta) = \left[ \begin{matrix}
1 & 0 & 0\
0 & cos\theta & -sin\theta\
0 & sin\theta & cos\theta
\end{matrix} \right]$$

  • 绕y轴旋转:

$$R_y(\theta) = \left[ \begin{matrix}
cos\theta & 0 & sin\theta\
0 & 1 & 0\
-sin\theta & 0 & cos\theta
\end{matrix} \right]$$

  • 绕z轴旋转:

$$R_z(\theta) = \left[ \begin{matrix}
cos\theta & -sin\theta & 0\
sin\theta & cos\theta & 0\
0 & 0 & 1
\end{matrix} \right]$$

  • 任意旋转矩阵:

任何一个旋转可以表示为依次绕着三个旋转轴旋三个角度的组合。这三个角度称为欧拉角。

由欧拉角求旋转矩阵

设三个轴$x,y,z$的欧拉角分别为$θ_x,θ_y,θ_z$,正弦值、余弦值分别为$s_x, c_x, s_y, c_y, s_z, c_z$那么旋转矩阵为:

$$R(\theta_x,\theta_y,\theta_z)=R_z(\theta_z)R_y(\theta_y)R_x(\theta_x) = \left[ \begin{matrix}
c_y c_z & c_z s_x s_y - c_x s_z & s_x s_z + c_x c_z s_y\
c_y s_z & c_x c_z + s_x s_y s_z & c_x s_y s_z - c_z s_z\
-s_y & c_y s_x & c_x c_y
\end{matrix} \right]$$

由旋转矩阵求欧拉角

$$R = \left[ \begin{matrix}
r_{11} & r_{12} & r_{13}\
r_{21} & r_{22} & r_{23}\
r_{31} & r_{32} & r_{33}
\end{matrix} \right] = \left[ \begin{matrix}
c_y c_z & c_z s_x s_y - c_x s_z & s_x s_z + c_x c_z s_y\
c_y s_z & c_x c_z + s_x s_y s_z & c_x s_y s_z - c_z s_z\
-s_y & c_y s_x & c_x c_y
\end{matrix} \right]$$

解方程可得:

$$\theta_x = atan2(r_{32}, r_{33})$$
$$\theta_y = atan2(-r_{31}, \sqrt{r_{32}^2+r_{33}^2})$$
$$\theta_z = atan2(r_{21}, r_{11})$$

注意:atan2()为C++中的函数,atan2(y,x) 求的是y/x的反正切,其返回值为[-pi,+pi]之间的一个数。

四元数

三维空间的任意旋转,都可以用绕三维空间的某个轴旋转过某个角度来表示,即所谓的Axis-Angle表示方法。这种表示方法里,Axis可用一个三维向量$(x,y,z)$来表示,$θ$可以用一个角度值来表示,直观来讲,一个四维向量$(θ,x,y,z)$就可以表示出三维空间任意的旋转。注意,这里的三维向量$(x,y,z)$只是用来表示axis的方向朝向,因此更紧凑的表示方式是用一个单位向量来表示方向axis,而用该三维向量的长度来表示角度值$θ$。这样以来,可以用一个三维向量$(θ∗x,θ∗y,θ∗z)$就可以表示出三维空间任意的旋转,前提是其中$(x,y,z)$是单位向量。这就是旋转向量(Rotation Vector)的表示方式,OpenCV里大量使用的就是这种表示方法来表示旋转(见OpenCV相机标定部分的rvec)。

  • 单位向量(x,y,z)旋转θ角度后的四元数:

$$(cos \frac{\theta}{2}, xsin \frac{\theta}{2}, ysin \frac{\theta}{2}, z*sin \frac{\theta}{2})$$

  • 对于三维坐标的旋转,可以通过四元数乘法直接操作,与旋转矩阵操作可以等价,但是表示方式更加紧凑,计算量也可以小一些。

四元数求旋转矩阵

已知四元数:

$$\mathbf{q} = q_0 + q_1 i + q_2 j + q_3 k = [s, \mathbf{v}]$$

利用Rodrigues公式可以由四元数求得旋转矩阵R:

$$R = \left[ \begin{matrix}
1 - 2 q_2^2 - 2 q_3^2 & 2q_1 q_2 - 2q_0 q_3 & 2 q_1 q_3 + 2 q_0 q_2 \
2q_1 q_2 + 2q_0 q_3 & 1 - 2 q_1^2 - 2 q_3^2 & 2 q_2 q_3 - 2 q_0 q_1 \
2 q_1 q_3 - 2 q_0 q_2 & 2 q_2 q_3 + 2 q_0 q_1 & 1 - 2 q_1^2 - 2 q_2^2
\end{matrix} \right ]$$

旋转矩阵求四元数

给出其中一种情况的计算方法:

$$q_0 = \frac{\sqrt{1+r_{11}+r_{22}+r_{33}}}{2}$$
$$q_1 = \frac{r_{32}-r_{23}}{4q_0}$$
$$q_2 = \frac{r_{13}-r_{31}}{4q_0}$$
$$q_3 = \frac{r_{21}-r_{12}}{4q_0}$$

其中要满足 $q_0 \neq 0$,$1+r_{11}+r_{22}+r_{33}>0$,即 $1+tr(R)>0$

四元数进行姿态变换

假设坐标系O1上的点P1(x1, y1, z1), 存在变换矩阵R, 可计算P1点在坐标系O2上的坐标值为P2(x2, y2, z2):
$$P2=R∗P1$$

矩阵R对应的四元数为q, 则使用四元数计算为:
首先三维空间点用一个虚四元数来描述:P1=[0, x1, y1, z1], P2=[0, x2, y2, z2]
则P2和P1将计算关系为:

$$P_2=qP_1q^{-1}$$

参考链接

  1. 四元数,by wikipedia.
  2. 四元数与空间旋转,by wikipedia.
  3. 从旋转矩阵计算欧拉角,by PengChao.
  4. 旋转变换(一)旋转矩阵,by csxiaoshui.
  5. 旋转矩阵、欧拉角、四元数理论及其转换关系,by jason_ql.
  6. 旋转变换(二)欧拉角,by csxiaoshui.
  7. 欧拉角,by wikipedia.
  8. 欧拉角细节/旋转顺序/内旋外旋,by 能儿.
  9. 四元数运算与姿态变换,by Yoyo_wym.
  10. Flight Parameters and Quaternion Math,by mathwork.

JSBSim源代码分析

发表于 2020-01-13

JSBSim是一个开源跨平台的飞行动力学模型(FDM)软件库,用于模拟航空航天飞行器的飞行动力学。 该库已被纳入飞行模拟软件包FlightGear和OpenEaagles。JSBSim可以独立运行,通过命令行参数指定飞行器和初始状态,进行简单情境下的飞行动力学仿真,也可以将JSBSim作为代码库,编程实现飞行器模型加载,设置输入,获得输出。下面将通过分析JSBSim源代码,研究其实现通用飞行动力学模型的方法。

入口分析

下面是JSBSim参考手册中的最简单实例,因JSBSim的不断开发,JSBSim参考手册中该编程实例有点过时,因此进行了少量修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <FGFDMExec.h>
#include <sg_path.hxx>

using namespace std;

int main(int argc, char **argv)
{
JSBSim::FGFDMExec FDMExec;
bool result = true;

FDMExec.LoadScript(SGPath::fromUtf8(argv[1]));

while (result) result = FDMExec.Run();
}

从上述代码可知,调用JSBSim的主要方法是利用FGFDMExec类,通过实例化一个FGFDMExec类,就相当于获得了一个运行JSBSim仿真的工具箱,通过这个工具箱就可以调用JSBSim的大部分功能,实现我们要的仿真目标。同时FGFDMExec类通过加载外部飞机的XML脚本,实现飞行动力学模型的通用性。

JSBSim初始化流程

上述JSBSim最简仿真示例中已包含其初始化流程,采用图示如下:

JSBSim初始化流程

图1 JSBSim初始化流程

FGFDMExec初始化

FGFDMExec类在其构造函数中对各个模型进行初始化,具体代码在Allocate函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr)
: Root(root), RandomEngine(new default_random_engine), FDMctr(fdmctr)
{
...
try {
Allocate();
} catch (const string& msg ) {
cout << "Caught error: " << msg << endl;
exit(1);
}

...
}

Allocate函数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
bool FGFDMExec::Allocate(void)
{
bool result=true;

Models.resize(eNumStandardModels);

// First build the inertial model since some other models are relying on
// the inertial model and the ground callback to build themselves.
// Note that this does not affect the order in which the models will be
// executed later.
Models[eInertial] = new FGInertial(this);

// See the eModels enum specification in the header file. The order of the
// enums specifies the order of execution. The Models[] vector is the primary
// storage array for the list of models.
Models[ePropagate] = new FGPropagate(this);
Models[eInput] = new FGInput(this);
Models[eAtmosphere] = new FGStandardAtmosphere(this);
...

// Assign the Model shortcuts for internal executive use only.
Propagate = (FGPropagate*)Models[ePropagate];
Inertial = (FGInertial*)Models[eInertial];
Atmosphere = (FGAtmosphere*)Models[eAtmosphere];
...

// Initialize planet (environment) constants
LoadPlanetConstants();

// Initialize models
for (unsigned int i = 0; i < Models.size(); i++) {
// The Input/Output models must not be initialized prior to IC loading
if (i == eInput || i == eOutput) continue;

LoadInputs(i);
Models[i]->InitModel();
}

...
return result;
}

Allocate函数代码中需要注意LoadInputs函数,该函数决定各个子模型的初始化顺序,确定各个子模型的输入输出,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
void FGFDMExec::LoadInputs(unsigned int idx)
{
switch(idx) {
case ePropagate:
Propagate->in.vPQRidot = Accelerations->GetPQRidot();
Propagate->in.vUVWidot = Accelerations->GetUVWidot();
Propagate->in.DeltaT = dT;
break;
case eInput:
break;
case eInertial:
Inertial->in.Position = Propagate->GetLocation();
break;
case eAtmosphere:
Atmosphere->in.altitudeASL = Propagate->GetAltitudeASL();
break;
case eWinds:
Winds->in.AltitudeASL = Propagate->GetAltitudeASL();
Winds->in.DistanceAGL = Propagate->GetDistanceAGL();
...
break;
case eAuxiliary:
Auxiliary->in.Pressure = Atmosphere->GetPressure();
Auxiliary->in.Density = Atmosphere->GetDensity();
...
break;
case eSystems:
// Dynamic inputs come into the components that FCS manages through properties
break;
case ePropulsion:
Propulsion->in.Pressure = Atmosphere->GetPressure();
...

break;
case eAerodynamics:
Aerodynamics->in.Alpha = Auxiliary->Getalpha();
...
break;
case eGroundReactions:
// There are no external inputs to this model.
GroundReactions->in.Vground = Auxiliary->GetVground();
...
break;
case eExternalReactions:
// There are no external inputs to this model.
break;
case eBuoyantForces:
BuoyantForces->in.Density = Atmosphere->GetDensity();
...
break;
case eMassBalance:
MassBalance->in.GasInertia = BuoyantForces->GetGasMassInertia();
MassBalance->in.GasMass = BuoyantForces->GetGasMass();
...
break;
case eAircraft:
Aircraft->in.AeroForce = Aerodynamics->GetForces();
Aircraft->in.PropForce = Propulsion->GetForces();
...
break;
case eAccelerations:
Accelerations->in.J = MassBalance->GetJ();
Accelerations->in.Jinv = MassBalance->GetJinv();
...
break;
default:
break;
}
}

FGFDMExec加载飞机配置

FGFDMExec的LoadScript函数在初始化时负责加载飞机配置,用于初始化各个子模型。

1
2
3
4
5
6
7
8
9
10
bool FGFDMExec::LoadScript(const SGPath& script, double deltaT,
const SGPath& initfile)
{
bool result;

Script = new FGScript(this);
result = Script->LoadScript(GetFullPath(script), deltaT, initfile);

return result;
}

FGFDMExec运行

FGFDMExec的Run函数负责飞行动力学模型的计算,其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool FGFDMExec::Run(void)
{
bool success=true;

...

// returns true if success, false if complete
if (Script != 0 && !IntegrationSuspended()) success = Script->RunScript();

for (unsigned int i = 0; i < Models.size(); i++) {
LoadInputs(i);
Models[i]->Run(holding);
}

...

return success;
}

FGFDMExec的Run函数将依次调用各个子模型的Run函数。

参考链接

  1. JSBSim编程实践之入门,by jackhuang.

排序算法总结

发表于 2020-01-05 | 更新于 2020-05-23

排序算法是计算机科学的基石之一,可从时间复杂度、空间复杂度、稳定性、是否原地排序等维度对排序算法进行分类。下面从时间复杂度方面对排序算法进行分类。

O(n^2)算法

冒泡排序

选择排序

插入排序

O(nlogn)算法

希尔排序

快速排序

归并排序

堆排序

O(n)算法

计数排序

桶排序

基数排序

参考链接

  1. 漫画:“排序算法” 大总结,by 小灰.
  2. 分布式哈希表 (DHT) 和 P2P 技术,by luyuhuang.
  3. Gzip 格式和 DEFLATE 压缩算法,by Luyu Huang.

Pandas入门教程

发表于 2020-01-02 | 更新于 2024-11-18

Pandas是一个开源的,BSD许可的库,为Python编程语言提供高性能,易于使用的数据结构和数据分析工具。

Pandas特色

Pandas 适用于处理以下类型的数据:

  • 与 SQL 或 Excel 表类似的,含异构列的表格数据;
  • 有序和无序(非固定频率)的时间序列数据;
  • 带行列标签的矩阵数据,包括同构或异构型数据;
  • 任意其它形式的观测、统计数据集, 数据转入 Pandas 数据结构时不必事先标记。

Pandas数据结构

Pandas 的主要数据结构是 Series(一维数据)与 DataFrame(二维数据),这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数典型用例。对于 R 用户,DataFrame 提供了比 R 语言 data.frame 更丰富的功能。Pandas 基于 NumPy 开发,可以与其它第三方科学计算支持库完美集成。

维数 名称 描述
1 Series 带标签的一维同构数组
2 DataFrame 带标签的,大小可变的,二维异构表格

Pandas用法

Pandas用法与Matlab中矩阵操作很类似,熟悉Matlab操作的同学可以很快上手Pandas。

生成对象

生成Series对象:

1
2
3
4
5
6
7
8
9
10
11
In [3]: s = pd.Series([1, 3, 5, np.nan, 6, 8])

In [4]: s
Out[4]:
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64

生成DataFrame对象:

1
2
3
4
5
6
7
8
9
10
11
In [7]: df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))

In [8]: df
Out[8]:
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
2013-01-05 -0.424972 0.567020 0.276232 -1.087401
2013-01-06 -0.673690 0.113648 -1.478427 0.524988

列切片

1
df1 = df[df.columns[0:6]];

重命名列名

1
df1.columns['id','name','sex','age','department','work']

过滤行

1
df1 = df1[df1.iloc[:,1]=='YES']

合并表格

pandas 2.0实现数据的合并与拼接,主要有三种方法:

  • join 最简单,主要用于基于索引的横向合并拼接
  • merge 最常用,主要用于基于指定列的横向合并拼接
  • concat最强大,可用于横向和纵向合并拼接
1
2
# 合并两个表,df1 和 df2 表结构一样
df3 = pd.concat([df1,df2])

毫秒解析

1
2
timeDelta = datetime.datetime(2024,11,11) - datetime.datetime(1900,1,1)
df3['datetime'] = pd.to_datetime(df['datetime'], format='%Y-%m-%d %H:%M:%S:%f') + timeDelta

按时间排序

1
df3 = df3.sort_values(by="datetime")

输出CSV表格

1
df3.to_csv(filePath + 'test.csv',index=0)

参考链接

  1. Pandas 中文,by pypandas.
  2. 十分钟入门 Pandas,by pypandas.
  3. Python读取csv文件的三种方式,by 涛声依旧2019.
  4. Python模块化开发组织代码程序示例,by BabyFish13.
  5. Python最佳实践指南!,by Prodesire.
  6. pandas中DataFrame 数据合并,连接(merge,join,concat) ,by Vincent-yuan.
  7. 【已解决】AttributeError: ‘DataFrame‘ object has no attribute ‘append‘,by zhtstar.
  8. pandas DataFrame数据重命名列名的几种方式,by littleRpl.
  9. Pandas 毫秒级时间解析,by 一定波兮.

jupyter notebook安装与使用

发表于 2019-12-31 | 更新于 2025-06-25

Jupyter Notebook(前身是IPython Notebook)是一个基于Web的交互式计算环境,用于创建Jupyter Notebook文档。Notebook一词可以通俗地引用许多不同的实体,主要是Jupyter Web应用程序、Jupyter Python Web服务器或Jupyter文档格式(取决于上下文)。Jupyter Notebook文档是一个JSON文档,遵循版本化模式,包含一个有序的输入/输出单元格列表,这些单元格可以包含代码、文本(使用Markdown语言)、数学、图表和富媒体,通常以“.ipynb”结尾扩展。

安装过程

安装前提

  • python>3.3 或者python=2.7

安装步骤

1
pip install notebook

启动Jupyter Notebook

1
jupyter notebook

使用指南

  • 单元格执行状态

单元格的执行状态对于复杂度高的代码,往往会意味着更长的执行等待时间。在Jupyter Notebook 中,当一个单元格处于执行状态时,单元格前面会出现 In [*] 符号,只有执行完成的单元格, [] 中的 * 才会变成相应的 序号。

除此之外,你可以通过页面右上角的 Kernel 状态指示器判断内核占用情况。如果 Python 字符右边出现了实心圆圈 ◉,代表内核处于占有状态。而空心圆圈 ◯ 则代表内核处于空闲状态。当然,也可能出现链接断开的符号,那就代表着内核已经断开链接,你可能需要刷新页面或重启实验环境。

参考链接

  1. Jupyter,by wikipedia.
  2. Installing the Jupyter Software,by jupyter.
  3. Matplotlib animation not working in IPython Notebook (blank plot),by stackoverflow.
  4. Jupyter Notebook使用指南,by zhanlang619.
  5. Jupyter中markdown的操作技巧,by 那一年_我九岁.

JavaScript正则表达式入门

发表于 2019-12-30 | 更新于 2019-12-31

最近在学习逐行剖析 Vue.js 源码的时候,发现Vuejs在模板编译时大量使用正则表达式。因此,将正则表达式的知识再温习一下。

基本概念

正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象。这些模式被用于 RegExp 的 exec 和 test 方法, 以及 String 的 match、matchAll、replace、search 和 split 方法。

基本语法

特殊字符

正则表达式中的特殊字符:

\

依照下列规则匹配:

在非特殊字符之前的反斜杠表示下一个字符是特殊字符,不能按照字面理解。例如,前面没有 “" 的 “b” 通常匹配小写字母 “b”,即字符会被作为字面理解,无论它出现在哪里。但如果前面加了 “",它将不再匹配任何字符,而是表示一个字符边界。

在特殊字符之前的反斜杠表示下一个字符不是特殊字符,应该按照字面理解。详情请参阅下文中的 “转义(Escaping)” 部分。

如果你想将字符串传递给 RegExp 构造函数,不要忘记在字符串字面量中反斜杠是转义字符。所以为了在模式中添加一个反斜杠,你需要在字符串字面量中转义它。/[a-z]\s/i 和 new RegExp(“[a-z]\s”, “i”) 创建了相同的正则表达式:一个用于搜索后面紧跟着空白字符(\s 可看后文)并且在 a-z 范围内的任意字符的表达式。为了通过字符串字面量给 RegExp 构造函数创建包含反斜杠的表达式,你需要在字符串级别和表达式级别都对它进行转义。例如 /[a-z]:\/i 和 new RegExp(“[a-z]:\\“,”i”) 会创建相同的表达式,即匹配类似 “C:" 字符串。

^

匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。

例如,/^A/ 并不会匹配 “an A” 中的 ‘A’,但是会匹配 “An E” 中的 ‘A’。

当 ‘^’ 作为第一个字符出现在一个字符集合模式时,它将会有不同的含义。反向字符集合 一节有详细介绍和示例。

$

匹配输入的结束。如果多行标示被设置为 true,那么也匹配换行符前的位置。

例如,/t$/ 并不会匹配 “eater” 中的 ‘t’,但是会匹配 “eat” 中的 ‘t’。

*

匹配前一个表达式 0 次或多次。等价于 {0,}。

例如,/bo*/ 会匹配 “A ghost boooooed” 中的 ‘booooo’ 和 “A bird warbled” 中的 ‘b’,但是在 “A goat grunted” 中不会匹配任何内容。

+

匹配前面一个表达式 1 次或者多次。等价于 {1,}。

例如,/a+/ 会匹配 “candy” 中的 ‘a’ 和 “caaaaaaandy” 中所有的 ‘a’,但是在 “cndy” 中不会匹配任何内容。

?

匹配前面一个表达式 0 次或者 1 次。等价于 {0,1}。

例如,/e?le?/ 匹配 “angel” 中的 ‘el’、”angle” 中的 ‘le’ 以及 “oslo’ 中的 ‘l’。

如果紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。例如,对 “123abc” 使用 /\d+/ 将会匹配 “123”,而使用 /\d+?/ 则只会匹配到 “1”。

还用于先行断言中,如本表的 x(?=y) 和 x(?!y) 条目所述。

.

小数点)默认匹配除换行符之外的任何单个字符。

例如,/.n/ 将会匹配 “nay, an apple is on the tree” 中的 ‘an’ 和 ‘on’,但是不会匹配 ‘nay’。

如果 s (“dotAll”) 标志位被设为 true,它也会匹配换行符。

\n

在正则表达式中,它返回最后的第n个子捕获匹配的子字符串(捕获的数目以左括号计数)。

比如 /apple(,)\sorange\1/ 匹配”apple, orange, cherry, peach.”中的’apple, orange,’ 。

\s

匹配一个空白字符,包括空格、制表符、换页符和换行符。等价于[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。

例如, /\s\w*/ 匹配”foo bar.”中的’ bar’。

\w

匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_]。

例如, /\w/ 匹配 “apple,” 中的 ‘a’,”$5.28,”中的 ‘5’ 和 “3D.” 中的 ‘3’。

\W

匹配一个非单字字符。等价于 [^A-Za-z0-9_]。

例如, /\W/ 或者 /[^A-Za-z0-9_]/ 匹配 “50%.” 中的 ‘%’。

(x)

像下面的例子展示的那样,它会匹配 ‘x’ 并且记住匹配项。其中括号被称为捕获括号。

模式 /(foo) (bar) \1 \2/ 中的 ‘(foo)’ 和 ‘(bar)’ 匹配并记住字符串 “foo bar foo bar” 中前两个单词。模式中的 \1 和 \2 表示第一个和第二个被捕获括号匹配的子字符串,即 foo 和 bar,匹配了原字符串中的后两个单词。注意 \1、\2、…、\n 是用在正则表达式的匹配环节,详情可以参阅后文的 \n 条目。而在正则表达式的替换环节,则要使用像 $1、$2、…、$n 这样的语法,例如,’bar foo’.replace(/(…) (…)/, ‘$2 $1’)。$& 表示整个用于匹配的原字符串。

标志

标志 描述
g 全局搜索。
i 不区分大小写搜索。
m 多行搜索。
s 允许 . 匹配换行符。
u 使用unicode码的模式进行匹配。
y 执行“粘性”搜索,匹配从目标字符串的当前位置开始,可以使用y标

使用方法

正则表达式可以被用于 RegExp 的 exec 和 test 方法以及 String 的 match、replace、search 和 split 方法。使用正则表达式的方法如下:

方法

方法 描述
exec 一个在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回 null)。
test 一个在字符串中测试是否匹配的RegExp方法,它返回 true 或 false。
match 一个在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null。
matchAll 一个在字符串中执行查找所有匹配的String方法,它返回一个迭代器(iterator)。
search 一个在字符串中测试匹配的String方法,它返回匹配到的位置索引,或者在失败时返回-1。
replace 一个在字符串中执行查找匹配的String方法,并且使用替换字符串替换掉匹配到的子字符串。
split 一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的 String 方法。

返回

对象 属性或索引 描述 在例子中对应的值
myArray 匹配到的字符串和所有被记住的子字符串。 [“dbbd”, “bb”]
myArray index 在输入的字符串中匹配到的以0开始的索引值。 1
myArray input 初始字符串。 “cdbbdbsbz”
myArray [0] 匹配到的所有字符串(并不是匹配后记住的字符串)。注:原文”The last matched characters.”,应该是原版错误。匹配到的最终字符。 “dbbd”
myRe lastIndex 下一个匹配的索引值。(这个属性只有在使用g参数时可用在 通过参数进行高级搜索 一节有详细的描述.) 5
myRe source 模式文本。在正则表达式创建时更新,不执行。 “d(b+)d”

示例

1
2
3
4
5
6
var re = /\w+\s/g;
var str = "fee fi fo fum";
var myArray = str.match(re);
console.log(myArray);

// ["fee ", "fi ", "fo "]

参考链接

  1. 逐行剖析 Vue.js 源码,by nlrx.
  2. 正则表达式,by mozilla.

编译原理学习笔记

发表于 2019-12-29

编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法。内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。

基本概念

  • 词法分析

从左到右逐个字符地扫描,从中识别出一个个“单词”符号。“单词”符号是程序设计语言的基本语法单位,如关键字、标识符、常数、运算符和分隔符等。

  • 语法分析

根据语言的语法规则将单词符号序列分解成各类语法单位,比如表达式、语句和程序等。语法规则就是各类语法单位的构成规则。通过语法分析确定整个输入串是否构成一个语法上正确的程序。

  • 语义分析

检查源程序是否包含静态语义错误,并收集类型信息供后面的代码生成阶段使用。只有语法和语义都正确的源程序才能被翻译成正确的目标代码。

语义分析的一个主要工作是进行类型分析和检查。程序语言中的一个数据类型一般包含两个方面的内容:类型的载体及其上的运算。例如:整除取余运算只能对整型数据进行运算,若其运算对象中有浮点数就认为是类型不匹配的错误。静态的语义错误是指编译程序可以发现,动态的语义错误是指源程序虽然能够被编译和执行,但是结果不对,一般是逻辑上的错误。

编译的过程

编译程序的工作过程一般可以分为5个阶段:

  1. 词法分析
  2. 语法分析
  3. 语义分析和中间代码的产生
  4. 优化
  5. 目标代码生成

参考链接

  1. AST 抽象语法树,by Jartto.
  2. 【编译原理】编译原理简单介绍,by cflys.
  3. 编译原理,by junhey.
上一页1…313233…54下一页

Jack Huang

535 日志
69 标签
© 2026 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse