乐趣区

关于流程图:程序流程图-规范与画图

Typora 反对应用 mermaid 进行绘图,其中就包含流程图。

日常生活中利用流程图的场景并不算太多,在写代码的过程中也更偏差应用伪代码。近期读《计算机程序设计艺术》卷一,上来就是一张流程图,不知怎的,忽然感觉流程图不可或缺。或者流程图搭配伪代码更好。

以上是我强行扯出的写本文的理由。

对于 mermaid

mermaid 泛滥代码绘图工具之一。最先接触的是 PlantUML,过后为了画类图交软件文档,但又不想手动画,于是搜寻到了这个工具。PlantUML 如其名,次要是 UML 图,反对的图较少,且依赖较多,配置的步骤较多。

mermaid 尽管没有 PlantUML 那样残缺反对 UML,但根本的图形都能绘制,且感激大前端的倒退,其只是一个“简略”的 .js 文件,导入浏览器导出应用,在 Markdown 这种兼容 HTML 的标记语言下,真心流程。

它反对简略的程序流程图,之所以说简略,是因为残缺的流程切实太过于简单。如果用不上,简单的就搁置吧,mermaid 曾经足够应用了。

装置 mermaid

参考 装置教程。

接触 mermaid 次要是 Typora 自身反对应用 mermaid,我的计划是在 Typora 实现编辑,随后导出 HTML,再到某个反对 Markdown 的平台进行公布。局部网站本身反对 mermaid

绘制到简略流程图如下:

graph LR
A([开始]) --> B[/ 编辑文章 /]
B --> C{内容实现?}
C -->| 是 | D{再编辑?}
C -->| 否 | B
D -->| 否 | Save[保留]
Save --> E{导出?}
D -->| 是 | B
E -->| 否 | End([完结])
E -->| 是 | ExportHTML[/ 导出 HTML/]
ExportHTML --> Publish[公布]
Publish --> End

Markdown 中应用 mermaid

Typora 应用 mermaid 很简略,间接引入代码即可:

​```mermaid
​```

而咱们须要应用流程图,能够参考文档,引入流程图间接应用 flowchart 或者 graph 关键字,同时须要明确指定流程图的绘制方向,TD 或者 TB 示意从上到下绘制,LR 示意从左到右绘制。(就是上下左右首字母示意)。而 flowchartgraph 可能应用的款式不一样,flowchart 绝对箭头比拟平滑,能用的箭头款式多样,graph 的款式绝对古板。咱们能够尝试一个简略的流程:

​```mermaid
graph LR
START([开始])
END([完结])

START --> A[测试]
A --> isRight{正确?}
isRight -->| 是 | END
isRight -->| 否 | A
​```

上述代码绘制如图:

graph LR
START([开始])
END([完结])

START --> A[测试]
A --> isRight{正确?}
isRight -->| 是 | END
isRight -->| 否 | A

咱们将 graph 替换成 flowchart 如图:

flowchart LR
START([开始])
END([完结])

START --> A[测试]
A --> isRight{正确?}
isRight -->| 是 | END
isRight -->| 否 | A

目前开来仅仅是款式存在区别,而文档中标注 flowchart 属于 Beta 状态,文档中只列举出了更多的箭头反对和子流程图反对。这并非咱们应该关注的重点,因此应用那个关键字,齐全在于集体爱好。flowchart 虽在文字上具备较为强烈的识别性,但 graph 绘制出了图像更清晰,在 Typora 中也有代码高亮,且 flowchat 提供的性能也用不上,因而后续应用 graph 作为关键字。

流程图根底

流程图的次要功能在于绝对文字能更直观形容某件事情的过程。

而应用代码实现流程图的构建,更像是伪代码去即时展示逻辑,当然也不便不相熟代码的人间接了解代码的基本思路(不晓得这有啥用)。绘制流程图,理清本人的逻辑是最次要的。

而人与人交换都须要一个标准,流程图自身有一个标准。

流程图符号

此处将列举惯例符号和 mermaid 对应的代码。

mermaid 并没有规定那个符号表明哪个意思,只是表述了该符号的形态。

1. 流程

示意执行或者解决某些工作。

mermaid 中流程的符号很简略,就是中括号 [],示例如下:

graph LR
A[流程 1]
B[流程 2]

A --> B

其成果如下:

graph LR
A[流程 1]
B[流程 2]

A --> B

mermaid 中,流程是一个根本的 node,它的形态是矩形,还有圆角矩形(应用 () 表述),两者区别不大,没有特定的要求,但 [] 更容易输出,因此应用 () 的必要不大。

2. 开始或完结

在 文档 中,所有开始或结符号都是应用流程间接代替的,并没有给出具体的要求。但流程图标准当中,开始和完结该当有别与流程的形态。在 mermaid 中应用 ([]) 能够绘制一个区别于流程的图形,如下:

graph LR
START([开始])
END([完结])
START --> END

后果如下:

graph LR
START([开始])
END([完结])
START --> END

文档 中,称之为 A stadium-shaped node,其形态相似于体育场。而开始和完结只有一个,在初始时将其定义好,后续能够间接应用。

特地留神,定义完结的时候,切勿应用小写 end,否则可能倒置流程图绘制中断

3. 断定

断定是对立的,mermaid 应用 {} 示意。如下:

graph LR
START([开始])
END([完结])

Flow[流程]
isTrue{正确?}

isTrue -->| 是 | END
isTrue -->| 否 | Flow

START --> Flow --> isTrue

成果如下:

graph LR
START([开始])
END([完结])

Flow[流程]
isTrue{正确?}
isTrue -->| 是 | END
isTrue -->| 否 | Flow

START --> Flow --> isTrue

如代码所示,咱们定义 isTrue 作为一个断定框,其内容是“正确?”,而正确与否的之后的流程如何解决很简略,间接在下一个前流程应用 | 是 / 否 | 即可。

4. 子流程

如其名,是一部分流程的汇合,次要功能在于简化整个流程图。而其外部的流程能够以附件形式展现到另一个流程图中。相似于子程序,mermaid 中为 A node in a subroutine shape。

代码如下:

graph LR
id[[子流程]]

成果如下:

graph LR
id[[子流程]]

即应用两个中括号示意,[[]]

5. 数据库

示意存储地位,属于扩大。

代码如下:

graph LR
DB[(数据库)]

成果如下:

graph LR
DB[(数据库)]

符号是 [()]

6. 页面内援用

即接口,示意两个流程图间的接口。

用处在于:

  1. 连贯到下一页;
  2. 防止流线穿插;
  3. 防止流线太长;

代码如下:

graph LR
OUT((接口))

成果如下:

graph LR
OUT((接口))

符号是 (())

7. 输出 / 输入

呈现输出或者输入的时候,应该区别于流程。

代码如下:

graph LR
input[/ 输出 /]

成果如下:

graph LR
input[/ 输出 /]

mermaid 还有反向的平行四边形,其符号与上述相同,[\\],成果如:

graph LR
input[\ 输出 \]

但咱们个别应用第一个。

8.(附加)箭头款式

流程图仿佛只有实线箭头,其余箭头很少见到,但 mermaid 提供了不少款式的箭头。

代码如下:

graph LR
A --- | 直线 | B
A ----- | 直线长度是 - 符号定义 - 长度 3 -5| B0
A --> | 直线箭头 | B1
A ---> | 长直线箭头 | B2
A === | 粗线 | B3
A ==> | 粗线箭头 | B4
A -.- | 虚线 | B5
A -.-> | 虚线箭头 | B6

成果如下:

graph LR
A --- | 直线 | B
A ----- | 直线长度是 - 符号定义 - 长度 3 -5| B0
A --> | 直线箭头 | B1
A ---> | 长直线箭头 | B2
A === | 粗线 | B3
A ==> | 粗线箭头 | B4
A -.- | 虚线 | B5
A -.-> | 虚线箭头 | B6

能够依据本人的需要应用。

当然,mermaid 还提供许多其余图形,包含梯形等等。这些请参考文档。

接下来咱们绘制下流程图的根底构造。

流程图的根本构造

流程图有三大根本构造,别离是:程序构造、抉择构造和循环构造,程序的形成也根本如此。在流程图绘制标准中,有规定这些构造的绘制办法,而利用 mermaid 绘制进去的图形仿佛并不那么标准。

1. 程序构造

程序构造即按程序先后顺序执行,从上往下 / 从左往右,一个一个执行。最根本的构造。

代码如下:

graph LR
START([开始])
END([完结])
START --> A
A --> B
B --> C
C --> END

成果如下:

graph LR
START([开始])
END([完结])
START --> A
A --> B
B --> C
C --> END

2. 抉择构造

抉择,即分支,即 if-else 构造:

graph TD

isTrueA{条件 A}
isTrueB{条件 B}
isTrueC{条件 C}

A([开始]) --> isTrueA
isTrueA --> | 是 | A1[步骤 1]
isTrueA --> | 否 | A2[步骤 2]
A1 --> AE([完结])
A2 --> AE

B([开始]) --> isTrueB
isTrueB --> | 是 | BE([完结])
isTrueB --> | 否 | B1[步骤 3]
B1 --> BE

C([开始]) --> isTrueC
isTrueC --> | 是 | C1[步骤 4]
isTrueC --> | 否 | CE([完结])
C1 --> CE

成果如下:

graph TD

isTrueA{条件 A}
isTrueB{条件 B}
isTrueC{条件 C}

A([开始]) --> isTrueA
isTrueA --> | 是 | A1[步骤 1]
isTrueA --> | 否 | A2[步骤 2]
A1 --> AE([完结])
A2 --> AE

B([开始]) --> isTrueB
isTrueB --> | 是 | BE([完结])
isTrueB --> | 否 | B1[步骤 3]
B1 --> BE

C([开始]) --> isTrueC
isTrueC --> | 是 | C1[步骤 4]
isTrueC --> | 否 | CE([完结])
C1 --> CE

能够看到,尽管咱们依照程序编写代码,但 条件 B 条件 C 的绘制中能够看出有步骤的状况在右,若两边都蕴含步骤,则按程序绘制。因为“是否”是依照 | 文字 | 嵌入的,则依照程序缺省行为执行绘制。

3. 循环构造

顾名思义,在肯定的条件下,其会实现反复动作。

循环构造又有当型和直到型,能够看作 whiledo while(! 条件)

代码:

graph TD

isTrue1{当型判断?}
isTrue2{直到型判断?}

A([开始]) --> isTrue1
isTrue1 --> | 是 | A1[步骤 1] --> isTrue1
isTrue1 --> | 否 | AE([完结])

B([开始]) --> B1[步骤 2]
B1 --> isTrue2
isTrue2 --> | 是 | BE([完结])
isTrue2 --> | 否 | B1

成果:

graph TD

isTrue1{当型判断?}
isTrue2{直到型判断?}

A([开始]) --> isTrue1
isTrue1 --> | 是 | A1[步骤 1] --> isTrue1
isTrue1 --> | 否 | AE([完结])

B([开始]) --> B1[步骤 2]
B1 --> isTrue2
isTrue2 --> | 是 | BE([完结])
isTrue2 --> | 否 | B1

都到这了,不思考什么画图标准了……

案例

来几个经典的案例。

1. 猜数字

猜数字 number,为随机数。

  1. 输出数字 n
  2. 判断大小,提醒大或者小
  3. 猜对提醒,是否持续
  4. 不持续退出,持续则从新生成 number,反复步骤 1 -4
graph TD
START([开始])
END([完结])

%% 该数字非输出,由系统生成
gen_number[生成随机数字 number-- 系统生成]
input_n[/ 输出数字 n /]

%% 定义判断条件
n_eq_number?{n == number?}
n_lt_number?{n < number?}
continue?{持续?}

%% 提醒
ALERT_gt[/ 提醒数字大了 /]
ALERT_eq[/ 提醒数字正确 /]
ALERT_lt[/ 提醒数字小了 /]

START --> gen_number --> input_n

input_n --> n_eq_number?

n_eq_number? --> | 是 | ALERT_eq --> continue?
n_eq_number? --> | 否 | n_lt_number?


n_lt_number? --> | 是 | ALERT_lt
n_lt_number? --> | 否 | ALERT_gt
n_lt_number? --> input_n

continue? --> | 是 | gen_number
continue? --> | 否 | END
graph TD
START([开始])
END([完结])

%% 该数字非输出,由系统生成
gen_number[生成随机数字 number-- 系统生成]
input_n[/ 输出数字 n /]

%% 定义判断条件
n_eq_number?{n == number?}
n_lt_number?{n < number?}
continue?{持续?}

%% 提醒
ALERT_gt[/ 提醒数字大了 /]
ALERT_eq[/ 提醒数字正确 /]
ALERT_lt[/ 提醒数字小了 /]

START --> gen_number --> input_n

input_n --> n_eq_number?

n_eq_number? --> | 是 | ALERT_eq --> continue?
n_eq_number? --> | 否 | n_lt_number?


n_lt_number? --> | 是 | ALERT_lt
n_lt_number? --> | 否 | ALERT_gt
n_lt_number? --> input_n

continue? --> | 是 | gen_number
continue? --> | 否 | END

逻辑有待优化。

2. 注册登录逻辑

1. 注册

graph TD
START([开始])
END([完结])

input[/ 输出帐号密码 /]

START --> input
input --> pw_pair?{明码是否合乎格局?}
pw_pair? --> | 否 | input
pw_pair? --> | 是 | user_pair?{帐号是否合乎格局?}

user_pair? --> | 是 | get_code[获取验证码]
user_pair? --> | 否 | input
get_code --> input_code[/ 输出验证码 /]
input_code --> code_pair?{验证码是否正确?}

code_pair? --> | 是 | success[注册胜利]
success -.- | 存储 | database[(用户数据库)]

code_pair? --> | 否 | 记录验证码谬误次数 --> try_more?{尝试次数过多?}

try_more? --> | 是 | will_END[限度获取验证码次数, 禁止注册] --> END
try_more? --> | 否 | en_time?{是否距离 60s 获取验证码?}

en_time? --> | 是 | get_code
en_time? --> | 否 | wait_time[期待 60s] --> get_code

success --> END
graph TD
START([开始])
END([完结])

input[/ 输出帐号密码 /]

START --> input
input --> pw_pair?{明码是否合乎格局?}
pw_pair? --> | 否 | input
pw_pair? --> | 是 | user_pair?{帐号是否合乎格局?}

user_pair? --> | 是 | get_code[获取验证码]
user_pair? --> | 否 | input
get_code --> input_code[/ 输出验证码 /]
input_code --> code_pair?{验证码是否正确?}

code_pair? --> | 是 | success[注册胜利]
success -.- | 存储 | database[(用户数据库)]

code_pair? --> | 否 | 记录验证码谬误次数 --> try_more?{尝试次数过多?}

try_more? --> | 是 | will_END[限度获取验证码次数, 禁止注册] --> END
try_more? --> | 否 | en_time?{是否距离 60s 获取验证码?}

en_time? --> | 是 | get_code
en_time? --> | 否 | wait_time[期待 60s] --> get_code

success --> END

2. 登录

graph TD

subgraph db [数据库]
db_user[(用户数据库)]
db_login[(已登录用户数据库)]
end

START([开始])
END([完结])

input[/ 输出帐号密码 /]

START --> input

input --> get_code[获取验证码]
get_code --> input_code[/ 输出验证码 /]
input_code --> code_pair?{验证码是否正确?}

code_pair? --> | 是 | pw_pair[从数据库匹配帐号密码]
pw_pair -.- | 查找 | db_user
pw_pair --> pw_pair?{帐号密码匹配?}

pw_pair? --> | 是 | success[登录胜利]
success -.- | 存储 | db_login
pw_pair? --> | 否 | input

code_pair? --> | 否 | 记录验证码谬误次数 --> try_more?{尝试次数过多?}

try_more? --> | 是 | will_END[限度获取验证码次数, 禁止登录] --> END
try_more? --> | 否 | en_time?{是否距离 60s 获取验证码?}

en_time? --> | 是 | get_code
en_time? --> | 否 | wait_time[期待 60s] --> get_code

success --> END
graph TD

subgraph db [数据库]
db_user[(用户数据库)]
db_login[(已登录用户数据库)]
end

START([开始])
END([完结])

input[/ 输出帐号密码 /]

START --> input

input --> get_code[获取验证码]
get_code --> input_code[/ 输出验证码 /]
input_code --> code_pair?{验证码是否正确?}

code_pair? --> | 是 | pw_pair[从数据库匹配帐号密码]
pw_pair -.- | 查找 | db_user
pw_pair --> pw_pair?{帐号密码匹配?}

pw_pair? --> | 是 | success[登录胜利]
success -.- | 存储 | db_login
pw_pair? --> | 否 | input

code_pair? --> | 否 | 记录验证码谬误次数 --> try_more?{尝试次数过多?}

try_more? --> | 是 | will_END[限度获取验证码次数, 禁止登录] --> END
try_more? --> | 否 | en_time?{是否距离 60s 获取验证码?}

en_time? --> | 是 | get_code
en_time? --> | 否 | wait_time[期待 60s] --> get_code

success --> END

在绝对简单的逻辑下,整体绘图有些芜杂。

小结

流程图不适用于过于简单的逻辑,对于简单的逻辑会有些表现力有余。前一章的登录界面我绘制了屡次,包含一张图体现登录注册登出等性能(应用 subgraph)当绘制实现之后,只管伪代码很容易了解,图像却盘根错节。

流程图更实用于体现逻辑关系,对于程序中一个性能进行形容(面向过程),而状态变动却不好表白。这又得引出一个状态流图。

而面向对象的类图又须要 UML 进行形容。

目前流程图可用于简略的繁多性能形容中,其余须要后续再进行总结。一个残缺的利用不会这么简略。

退出移动版