想让 AI 给出有用的代码解释,就要说明你已经掌握了多少,要求逐行解释,并要求给出一份跟踪:一张表格,列出代码运行时每个变量的值。只说一句“解释这段代码”,得到的是为一个想象中的普通读者写的概述,对你来说通常要么太浅,要么太快。
逐行解释,并跟踪变量的值
两个标签页问的是同一个简短的函数。比较一下每条回复能让你检查什么。
def running_max(nums):
result = []
best = None
for n in nums:
if best is None or n > best:
best = n
result.append(best)
return result
print(running_max([3, 1, 4, 1, 5]))这个函数计算列表的累计最大值。它遍历这些数字,用 best 记录到目前为止见过的最大值,并在每一步把这个值追加到 result 中。best is None 这个判断用来处理第一个元素。这次调用会打印 [3, 3, 4, 4, 5]。
这个函数的时间复杂度是 O(n),因为它只访问每个元素一次。
第一条回复是准确的,但只有在你已经知道累计最大值是什么时才有帮助。它还加了一段没人问的时间复杂度说明,当模型不知道读者是谁时,就会多出这类内容。第二条回复把每句话都对应到你能指出来的某一行,而表格把概述中一笔带过的内容变得具体:result.append(best) 在每次循环中都会运行,包括 best 没有变化的那几次。这就是 3 出现两次的原因,你可以在第 2 行直接看到,而不用从“在每一步”这几个字中去推断。
跟踪表也是对解释本身的一种检查。你可以在循环里加一行 print(n, best, result) 运行代码,来核对每一行。如果某一行和真实的输出不一致,说明解释有地方错了,而现在你知道错在哪里了。
按你的水平解释
解释类提示词中最有用的一句话,就是说明你已经知道什么。它告诉模型哪些行可以跳过、哪些行要放慢讲。一句角色设定也有帮助(“你是一位老师,面向刚开始学编程第一个月的人”),但它的作用机制是一样的:告诉模型读者是谁。角色提示介绍了角色能改变什么、不能改变什么。
下面这个代码块是一个可复用的版本。改变水平、困惑的部分或代码,解释也会随之改变。
text = "the cat and the hat"
counts = {}
for word in text.split():
counts[word] = counts.get(word, 0) + 1
top = sorted(counts.items(), key=lambda kv: kv[1], reverse=True)[:2]
print(top)循环统计每个单词出现的次数:counts 最后是 {'the': 2, 'cat': 1, 'and': 1, 'hat': 1}。
现在从里到外看最后两行:
counts.items()给出这些键值对:('the', 2)、('cat', 1)、('and', 1)、('hat', 1)。lambda kv: kv[1]是一个没有名字的小函数。它接收一个键值对,返回其中的第二项,也就是次数。对于('the', 2),它返回2。sorted(..., key=...)按这个函数的返回值对键值对排序,也就是按次数排序。reverse=True把最大的放在最前面。次数相同的单词保持原来的顺序。[:2]保留前两个键值对。
所以 top 是 [('the', 2), ('cat', 1)],打印出来的就是它。
“次数相同的单词保持原来的顺序”正是快速解释会漏掉的细节,也正是 cat 拿到第二名、而不是 and 或 hat 的原因。Python 的 sorted 是稳定排序,而 reverse=True 会保持这种稳定性。你不会想到要问这一点,是“展示每一部分针对这个输入得出的值”这个要求,让它浮出了水面。
问一个部分,而不是整个文件
粘贴一个 500 行的文件,问“这是做什么的”,得到的是对文件结构的一番导览。当你卡在某个函数上时,就粘贴这个函数以及调用它的那几行,然后直接问它:“为什么列表为空时这个函数会提前返回?”或者“第 12 行的 yield 是做什么的?如果换成 return 会有什么变化?”具体的问题得到的是关于你的代码的回答;宽泛的问题得到的是关于类似代码的回答。
面对一个庞大而陌生的代码库,换一种方式:先要一张地图(“列出主要的模块,以及每个模块负责什么,每个一行”),再去问你需要的那个模块。这就是一个小小的提示词链,每一步都让模型的回答短到足以核实。
借助你会的语言来解释
用一门不熟悉的语言写的代码,在解释以你会的语言为参照时会更容易读懂。在提示词里说明这一点:“我会 Python。请解释这个 Go 函数,并指出每一处和 Python 程序员的预期不同的地方。”误解就藏在这些差异里:整数除法、零值而不是 None、与来源数组共享内存的切片。对这段 Go 代码的普通解释,会正确地描述这些行,却永远不会告诉你,你对它们的直觉是错的。
检查你是否真的理解了
一个你读过、也认同的解释,仍然可能让你没法自己写出这段代码。有两个提示词可以弥补这个差距:
- 先预测。 在运行代码或要求解释之前,先写下你认为它会打印什么。然后让模型只解释你预测错了的那些行。
- 讲回去。 写下你自己的解释,让模型指出其中错误或遗漏的地方,但不要重写它。
def running_max(nums):
result = []
best = None
for n in nums:
if best is None or n > best:
best = n
result.append(best)
return result
问我 3 个问题,检查我是否真的理解了它,一次问一个。其中要有一道题让我预测一个新输入的输出。等我回答之后再问下一题,并告诉我答得对不对、为什么。第 1 题,共 3 题:running_max([2, 7, 5, 9]) 会返回什么?先自己算出来,再运行代码。
同样的方法也适用于程序出了问题的时候,不过那时你想要的是原因,而不是一堂课:见用于调试的提示词。当你面前的代码是 AI 写的时,逐行解释是在保留它之前快速审查一遍的好办法;用于写代码的提示词列出了其他需要做的检查。
常见问题
怎么让 ChatGPT 或 Claude 解释代码?
粘贴代码,然后说明三件事:你已经知道什么、哪一部分让你困惑,以及你希望解释是什么形式。“用一句话解释每一行,然后用表格列出每次循环后各个变量的值”得到的回答,远比“解释这段代码”有用得多。
跟踪表是什么?
跟踪表记录程序运行的过程:每一步(通常是每次循环)一行,每个变量一列,填上该变量在那一刻的值。要求给出跟踪表,能把抽象的描述变成可以对照代码检查的具体数值。它就是你用调试器单步执行程序时所看到内容的书面记录。
AI 能解释我不会的编程语言写的代码吗?
能,而且这是最值得让它做的事情之一。说出一门你会的语言,让模型做对比:“我会 Python,请解释这段 Rust 代码,并指出它和 Python 行为不同的地方。”关于语言行为的说法,要通过运行小例子来核实,因为解释可能会在细节上错得理直气壮。
用 AI 解释代码对学习有坏处吗?
这取决于你接下来做什么。读完解释就继续往下走,学不到多少东西。在运行代码之前先预测输出、让模型给你出题,或者把代码讲给模型听并问它你哪里讲错了,这些都需要你自己动脑思考,而学习正是在这里发生的。