# 玩转递归实及AI实践篇

# 递归的执行过程
有一颗二叉树:

下面代码的执行过程是怎么样的?
func TestF(t *testing.T) {
// 建立二叉树
root := &TreeNode{Value:"a"}
root.Left = &TreeNode{Value:"b"}
root.Right = &TreeNode{Value:"c"}
root.Left.Left= &TreeNode{Value:"e"}
root.Left.Right= &TreeNode{Value:"f"}
root.Right.Left= &TreeNode{Value:"g"}
root.Right.Right= &TreeNode{Value:"h"}
// 传入根节点
F(root)
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
type TreeNode struct {
Value string
Left *TreeNode
Right *TreeNode
}
func F(root *TreeNode) {
if root == nil {
return
}
// 1. 第一次来到当前节点 root
F(root.Left)
// 2. 第二次来到当前节点 root
F(root.Right)
// 3. 第三次来到当前节点 root
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可视化:

# 递归在二叉树上的应用
二叉树是一种常见的数据结构,其节点最多有两个子节点:左子节点和右子节点。在对二叉树进行遍历时,我们有三种常见的方式:
- 前序遍历(Pre-order Traversal): 任何子树的处理顺序都是,先头节点、再左子树、然后右子树
- 中序遍历(In-order Traversal): 任何子树的处理顺序都是,先左子树、再头节点、然后右子树
- 后序遍历(Post-order Traversal): 任何子树的处理顺序都是,先左子树、再右子树、然后头节点
# 递归套路模板
# 文件夹目录上的使用
文件目录树:
// 主函数
func (p *Parser) Parse(localDirPath string) {
pathList := dir.ToPathList(localDirPath)
p.PreProcess(pathList, []string{}, utility.GetUuidByString(pathList[len(pathList)-1]), 0)
}
func (p *Parser) PreProcess(
path []string, // 建议添加,根据需要
ids []string, // 业务参数
myId string, // 业务参数
endLayer int // 业务参数
) bool {
if !dir.IsDir(dir.ToPathStr(path)) {
// 递归终止条件,子节点处理
return 返回值
}
// 文件夹路径
pathStr := dir.ToPathStr(path)
files := dir.GetFiles(pathStr)
for _, v := range files {
// 记录路径 ---- 非常有用,方便定位问题
path = append(path, v)
ids = append(ids, myId)
isHasCase = p.PreProcess(path, ids, utility.GetUuidByString(myId+v), endLayer) || isHasCase
// 路径回退
path = path[:len(path)-1]
ids = ids[:len(ids)-1]
}
return isHasCase
}
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
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
# 字典格式上的使用
# 需求简介

# GPT代码
提示词:
我有一个VUE2+TS的项目,现在有一个需求:
需要识别字符串中@{xxx}的数量,举个例子:
```
"asdfsadf@{xxx}adfasdf@{yyy}df@{xxx}"
```
我需要得到的结果是一个对象:
```
{
@{xxx}: 2
@{yyy}: 1
}
```
请你根据需求帮我完善代码
```
public getTextVariables(locationNodeInput: string): Record<string, number> {
// 再这里完善代码,可以拆分为多个函数
return {};
}
```
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
获取设备所有类型的选择项示例:
public static getEnvConfigDeviceTypeKeyKeys(envId: string, targetType: string, targeteKey: string) {
const curConfig = this.envConfigs[envId]
const uniqueKeys: Set<string> = new Set();
// 遍历数据对象
for (const key in curConfig) {
if (curConfig.hasOwnProperty(key)) { // 遍历属性
const device = curConfig[key]; // 找到所有设备
if (device.type === targetType) { // 找到符合条件的设备
for (const deviceKey in device) { // 遍历符合条件的设备的属性
if (device.hasOwnProperty(deviceKey) && deviceKey==targeteKey) { // 找到目标属性
const targeteKeyMap = device[targeteKey] // 获取到目标属性的map
for (const deviceKey in targeteKeyMap) { // 遍历目标属性的map将key获取到
uniqueKeys.add(deviceKey);
}
}
}
}
}
}
// 将 Set 转换为数组并返回
return Array.from(uniqueKeys);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
如果是属性呢?即使是同一类设备因为有自定义属性,所以可能同一类设备的属性字段有些有有些没有,有的话还能继续嵌套属性,代码如果这么写,如何维护?
# 模板代码
// 主函数合并有值的属性
public static mergeDevicesFeildsConfig(envId: string) {
let devicesFeildsMap = {}
const curConfig = this.envConfigs[envId]
const path: string[] = [] // 记录路径信息
for (let deviceId of Object.keys(curConfig)) { // 遍历所有设备
let device = curConfig[deviceId]
path.push(device[TYPE]) // 路径信息为设备类型
this.mergeFeildProcess(device["attr"], devicesFeildsMap, path) // 递归处理
path.pop()
}
this.devicesMergeEnvConfigs[envId] = devicesFeildsMap // 设置缓存,便于值查询
return devicesFeildsMap
}
private static mergeFeildProcess(deviceAttrs: any, devicesFeildsMap: any, path: string[]) {
if (deviceAttrs == undefined) {// 异常防护,有些设备没有attr 或为null的字段,如:BizCode
// 代码异常的时候直接打印path可以定位问题字段,而不是抓瞎
return
}
for (let attr of Object.keys(deviceAttrs)) { // 循环遍历所有的属性
if (typeof deviceAttrs[attr] !== 'object' || Array.isArray(deviceAttrs[attr])) { // 如果不是对象或是列表类型,收集value并终止
path.push(attr)
this.setDevicesFeildsMapByPath(path, deviceAttrs[attr], devicesFeildsMap)
path.pop()
} else { // 还是属性,不需要处理,继续深入
path.push(attr)
this.mergeFeildProcess(deviceAttrs[attr], devicesFeildsMap, path)
path.pop()
}
}
}
private static setDevicesFeildsMapByPath(path: string[], value: any, devicesFeildsMap: any) { // 根据path信息,将我们收集的值设置到设备字段映射中
let strValue = this.stringifyOrReturn(value)
let curMap = devicesFeildsMap // 从根map开始根据path,深入,填充value值
for (let i=0; i<path.length; i++) {
let key = path[i]
if (i === path.length - 1) { // 最后一级的时候,填充值
if (!curMap[key]) { // 不存在的时候
curMap[key] = [strValue]
} else {
if (!curMap[key].includes(strValue)) {
curMap[key].push(strValue)
}
}
} else { //非最后一级的时候,补充路径
if (!curMap[key]) {
curMap[key] = {}
}
curMap = curMap[key]
}
}
}
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
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
# 没有设计怎么办?
设定角色:
# 角色
你是一名VUE2+TS项目开发的专家。你阅读过所有软件开发的相关书籍,如《clean code》、《领域驱动设计》、《代码重构》、《设计模式》等。你熟练掌握面向对象编程,并能根据需求场景选择使用合适的设计模式。
## 技能
### 技能1:项目开发
- 根据用户的需求,进行VUE2+TS项目的开发。
### 技能2:代码优化
- 使用《clean code》中的原则,让你的代码整洁,易读。
### 技能3:领域驱动设计
- 运用《领域驱动设计》的理念,明确业务模型,以产生更具有价值的软件。
### 技能4:代码重构
- 根据《代码重构》的方法,不析构软件的外在行为的前提下,改变其内部结构。
### 技能5:设计模式的运用
- 阅读过《设计模式》,根据需求场景选择使用合适的设计模式。
## 限制
- 只讨论与Vue2+TS项目开发相关的问题。
- 始终遵守《clean code》、《领域驱动设计》、《代码重构》、《设计模式》等书中的原则和准则。
- 无法面对面与用户进行交流,所有的交流必须通过文字进行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
再给提示词:
我有一个VUE2+TS的项目,现在有一个需求:
需要识别字符串中@{xxx}的数量,举个例子:
```
"asdfsadf@{xxx}adfasdf@{yyy}df@{xxx}"
```
我需要得到的结果是一个对象:
```
{
@{xxx}: 2
@{yyy}: 1
}
```
请你根据需求帮我完善代码
```
public getTextVariables(locationNodeInput: string): Record<string, number> {
// 再这里完善代码,可以拆分为多个函数
return {};
}
```
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
结果:
先返回:
明白了!如果你有任何关于Vue2+TS项目开发的问题或者需要帮助,随时告诉我!
1
再返回:
好的,我可以帮你完成这个需求。根据你的描述,我们可以编写一个函数来实现字符串中"@{name}"的替换。你希望代码整洁易读,我们可以遵循《clean code》的原则,尽量拆分为多个函数,每个函数负责一个明确的任务。我们也可以考虑使用正则表达式来匹配"@{name}"这样的模式。以下是一个可能的实现:
public deleteCodeTextVariable(locationNodeInput: string): string {
return this.replaceVariablesWithEmptyString(locationNodeInput, "@{name}");
}
private replaceVariablesWithEmptyString(input: string, variablePattern: string): string {
const regex = new RegExp(variablePattern, 'g');
return input.replace(regex, '');
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 两份config找不同
题目:网管配置文件,有两个版本,想找出两份配置不同的参数,以及对应的路径。
# 开发小妙招
- 平时使用
GPT时记录promt提示词,做好分类,遇到同样的问题,快速解决 - 如果可以打造一条机器人的自动化流水线