Appearance
第三部分:代码调试(Debugging)专项技巧
代码调试是软件开发过程中的重要环节,AI 可以成为开发者的得力助手。以下是几种有效的代码调试提示词技巧:
1. 报错喂入法:正确处理 StackTrace 和日志
基本原理
报错喂入法是指将完整的错误信息、堆栈跟踪(StackTrace)和相关日志提供给 AI,让 AI 分析错误原因并提供修复方案。
关键要素
- 完整的错误信息:包括错误类型、错误消息和完整的堆栈跟踪
- 相关代码:与错误相关的代码片段
- 运行环境:运行时环境、依赖版本等信息
- 重现步骤:如何重现该错误
实战示例
输入示例
Role: 你是一位资深的 JavaScript 调试专家,擅长分析和解决各种运行时错误。
Context:
- 技术栈:Node.js 16 + Express
- 错误信息:TypeError: Cannot read properties of undefined (reading 'name') at getUserDetails (/path/to/app.js:15:20) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async /path/to/app.js:25:10
- 相关代码:
```javascript
// app.js
const express = require('express');
const app = express();
function getUserDetails(user) {
return user.name; // 第15行
}
app.get('/user/:id', async (req, res) => {
const user = await fetchUser(req.params.id);
const details = getUserDetails(user); // 第25行
res.json(details);
});
async function fetchUser(id) {
// 模拟数据库查询
if (id === '123') {
return { id: '123', name: 'John Doe' };
}
return null; // 当用户不存在时返回 null
}Task:
- 分析错误原因
- 提供修复方案
- 确保代码在用户不存在时能够正确处理
Output Format:
- 首先分析错误原因
- 然后提供修复后的代码
- 最后解释修复方案
#### 输出示例错误分析
错误类型:TypeError: Cannot read properties of undefined (reading 'name')
错误原因:
- 在
getUserDetails函数中,尝试访问user.name,但user参数为undefined - 当
fetchUser函数返回null时(用户不存在),getUserDetails(null)会导致此错误
修复方案
javascript
// app.js
const express = require('express');
const app = express();
function getUserDetails(user) {
if (!user) {
return { error: 'User not found' };
}
return user.name;
}
app.get('/user/:id', async (req, res) => {
const user = await fetchUser(req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const details = getUserDetails(user);
res.json(details);
});
async function fetchUser(id) {
// 模拟数据库查询
if (id === '123') {
return { id: '123', name: 'John Doe' };
}
return null; // 当用户不存在时返回 null
}修复解释
- 在
getUserDetails函数中:添加了空值检查,当user为null或undefined时返回错误信息 - 在路由处理函数中:添加了用户存在性检查,当用户不存在时直接返回 404 状态码和错误信息
- 这样处理:避免了尝试访问不存在对象的属性,同时提供了更友好的错误提示
### 提示词模板Role: 你是一位资深的 [语言/框架] 调试专家,擅长分析和解决各种运行时错误。
Context:
- 技术栈:[技术栈信息]
- 错误信息:
[完整的错误信息和堆栈跟踪] - 相关代码:
[与错误相关的代码片段]
Task:
- 分析错误原因
- 提供修复方案
- 确保代码能够正确处理类似情况
Output Format:
- 首先分析错误原因
- 然后提供修复后的代码
- 最后解释修复方案
## 2. 假定修复法:引导 AI 提出多个解决方案
### 基本原理
假定修复法是指引导 AI 分析问题并提出多个可能的解决方案,然后对这些方案进行评估和比较,选择最佳方案。
### 实施步骤
1. **描述问题**:详细描述代码中存在的问题
2. **要求多个解决方案**:明确要求 AI 提供多个可能的修复方案
3. **评估标准**:提供评估解决方案的标准(如性能、可读性、维护性等)
4. **方案比较**:要求 AI 对各个方案进行比较和分析
5. **推荐方案**:要求 AI 推荐最佳解决方案
### 实战示例
#### 输入示例Role: 你是一位资深的 Python 性能优化专家,擅长分析和解决代码性能问题。
Context:
- 技术栈:Python 3.9
- 问题描述:以下代码在处理大型列表时性能较差
- 相关代码:python
def process_large_list(data): result = [] for item in data: if item % 2 == 0: squared = item ** 2 result.append(squared) return result # 调用示例 large_list = list(range(1000000)) result = process_large_list(large_list)
Task:
- 分析代码性能问题
- 提供至少 3 种不同的优化方案
- 对每种方案进行性能、可读性和维护性的评估
- 推荐最佳解决方案
Output Format:
- 首先分析性能问题
- 然后提供 3 种优化方案
- 对每种方案进行评估
- 最后推荐最佳方案
#### 输出示例性能问题分析
当前代码的性能问题:
- 使用了显式的 for 循环,Python 中的 for 循环相对较慢
- 对每个元素进行条件判断和计算
- 列表 append 操作虽然高效,但仍有优化空间
优化方案
方案 1:使用列表推导式
python
def process_large_list(data):
return [item ** 2 for item in data if item % 2 == 0]评估:
- 性能:显著提升,列表推导式比显式 for 循环更快
- 可读性:代码更简洁,可读性好
- 维护性:代码量减少,维护成本降低
方案 2:使用 filter 和 map 函数
python
def process_large_list(data):
return list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, data)))评估:
- 性能:与列表推导式相当
- 可读性:函数式编程风格,对一些开发者来说可能不如列表推导式直观
- 维护性:代码简洁,但逻辑可能不如列表推导式清晰
方案 3:使用 NumPy 库
python
import numpy as np
def process_large_list(data):
arr = np.array(data)
even_arr = arr[arr % 2 == 0]
return (even_arr ** 2).tolist()评估:
- 性能:对于大型列表,NumPy 通常比纯 Python 实现快得多
- 可读性:代码清晰,表达意图明确
- 维护性:依赖 NumPy 库,增加了项目依赖
推荐方案
推荐方案 1:使用列表推导式
理由:
- 性能提升显著,与其他方案相当或更好
- 代码简洁易读,符合 Python 风格
- 不引入额外依赖,保持代码轻量
- 维护成本低,逻辑清晰
对于特别大的数据集(如超过 1000 万元素),可以考虑方案 3 使用 NumPy 进一步提升性能。
### 提示词模板Role: 你是一位资深的 [语言/框架] 专家,擅长分析和解决 [具体问题类型] 问题。
Context:
- 技术栈:[技术栈信息]
- 问题描述:[详细描述问题]
- 相关代码:
[相关代码片段]
Task:
- 分析问题原因
- 提供至少 3 种不同的解决方案
- 对每种方案进行 [评估标准] 的评估
- 推荐最佳解决方案
Output Format:
- 首先分析问题
- 然后提供多种解决方案
- 对每种方案进行评估
- 最后推荐最佳方案
## 3. 逐步排查法:利用单元测试复现 Bug
### 基本原理
逐步排查法是指利用提示词让 AI 编写单元测试来复现 Bug,然后通过测试来验证修复方案的有效性。
### 实施步骤
1. **描述 Bug**:详细描述 Bug 的现象和预期行为
2. **编写测试**:要求 AI 编写单元测试来复现 Bug
3. **实现修复**:基于测试结果,实现修复方案
4. **验证修复**:运行测试验证 Bug 是否被修复
5. **回归测试**:确保修复不会引入新的问题
### 实战示例
#### 输入示例Role: 你是一位资深的 Java 测试专家,擅长编写单元测试和调试代码。
Context:
- 技术栈:Java 11 + JUnit 5
- Bug 描述:当输入为空字符串时,
StringUtils.capitalize()方法会抛出 NullPointerException - 相关代码:java
public class StringUtils { public static String capitalize(String str) { if (str.length() == 0) { return str; } return Character.toUpperCase(str.charAt(0)) + str.substring(1); } }
Task:
- 编写单元测试来复现这个 Bug
- 修复 StringUtils.capitalize() 方法
- 验证修复是否成功
- 确保修复不会破坏现有功能
Output Format:
- 首先编写单元测试来复现 Bug
- 然后提供修复后的代码
- 最后验证修复结果
#### 输出示例单元测试
java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class StringUtilsTest {
@Test
public void testCapitalizeWithEmptyString() {
// 测试空字符串,应该返回空字符串而不是抛出异常
assertDoesNotThrow(() -> {
String result = StringUtils.capitalize("");
assertEquals("", result);
});
}
@Test
public void testCapitalizeWithNull() {
// 测试 null,应该返回 null 或抛出明确的异常
assertDoesNotThrow(() -> {
String result = StringUtils.capitalize(null);
assertNull(result);
});
}
@Test
public void testCapitalizeWithNormalString() {
// 测试正常字符串,应该正确大写首字母
assertEquals("Hello", StringUtils.capitalize("hello"));
assertEquals("World", StringUtils.capitalize("World"));
}
}修复方案
java
public class StringUtils {
public static String capitalize(String str) {
if (str == null) {
return null;
}
if (str.length() == 0) {
return str;
}
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}
}验证结果
- 测试空字符串:现在可以正确处理空字符串,返回空字符串而不是抛出异常
- 测试 null:现在可以正确处理 null,返回 null 而不是抛出异常
- 测试正常字符串:功能保持不变,仍然可以正确大写首字母
修复成功,同时保持了向后兼容性。
### 提示词模板Role: 你是一位资深的 [语言/框架] 测试专家,擅长编写单元测试和调试代码。
Context:
- 技术栈:[技术栈信息]
- Bug 描述:[详细描述 Bug 现象]
- 相关代码:
[相关代码片段]
Task:
- 编写单元测试来复现这个 Bug
- 修复 [类名].[方法名] 方法
- 验证修复是否成功
- 确保修复不会破坏现有功能
Output Format:
- 首先编写单元测试来复现 Bug
- 然后提供修复后的代码
- 最后验证修复结果
通过这些专项技巧,开发者可以更有效地利用 AI 进行代码调试,提高开发效率和代码质量。