简介

这篇文章是最近在查资料时发现的一篇文章。我觉得非常有意思。作者在这篇文章中介绍了在平时调试程序时可以使用的一些推理方法,学习这些方法可以更好的帮助我们在脑海中形成一个有用的框架,让我们以一种更有条理的方式调试程序。

需要说明的是,这篇文章是使用最近发现的一个叫DeepL翻译器做机器翻译然后我再适当修改形成的,可能有些地方比较奇怪。但不得不说,这个翻译功能着实强大,翻译效果很不错。有兴趣的朋友也可以自己去尝试一下。

文章来源 thoughtbot:Classical Reasoning and Debugging

正文

当古典哲学家们在思考如何思考时,他们开始研究一个人如何建立一个复杂的论证并证明或反驳一个想法的各种方法。这就孕育了推理和逻辑的研究,作为一个学科。

他们确定了一些广泛的逻辑思维方式的类别。在调试时,这些方法可以形成一个有用的框架,因为它给了我们一种方法来有条不紊地寻找真相:为什么会出现这种情况呢?

在古典哲学中,并没有一个明确的推理类型清单。下面是我在自己的经验中发现的一些特别有帮助的类型。我也对每种类型的定义做了一些松散的处理,以保持例子的易懂和有用。


类比推理法

这是我最喜欢的方法之一! 类比推理是一个三步走的过程。

  1. 将你的难题转化为等价的简单问题(称为类比)。
  2. 解决这个简单的问题。
  3. 将简单问题的解决方案转换为困难问题的解决方案。

在调试中应用这个方法的一个特别有用的方法是将一个错误减小到其最简单的形式。在你的上下文中,"最小 "的意思可能并不总是很明显。没关系。你可以慢慢地把复杂的部分一个一个地去掉,同时验证该错误是否仍然发生。最终,你将会留下一段小得多的代码。现在应该更容易找到错误的原因了。解对于整个问题,解决办法可能是相同的。

当处理一个更大的系统中的错误而不是一个单一的文件时,这样做可能会更困难,因为这类错误往往是由多个组件之间的交互引起的。比方说,在添加了一个图像处理 gem 之后,你注意到了 Rails 应用程序中的错误行为。这个 gem 坏了吗?你是否对它进行了错误的配置?你的一些其他现有代码与新的gem不兼容吗?

你怎么能简化这个问题呢?一种方法可能是生成一个全新的空白Rails应用程序,并添加可疑的gem。如果问题出现在这里,你现在就有一个小得多的应用程序可以调试。一旦你找到了解决方案,就可以把它移植到你真正的应用中。

除了简化你现有的环境外,类比还允许你将你的错误转移到一个有一些相似之处的不同环境中,在那里你可以更好地进行调试。例如,在这个Elm调试故事中,我能够将合成随机生成器时的问题重现为合成函数时的问题。我对函数组合的属性比较熟悉,很快就找到了错误的根源。然后我把这个解决方案回传到我原来的随机生成器问题上,就能解决这个问题了。


排除法

有时候,排除错误的答案比找到正确的答案更容易。排除过程属于归纳推理的范畴(过于简单了!)。

在调试中,这种方法一个特别有用的形式是二分搜索。这个想法是,你进行一系列的实验来验证一个错误是否是由某段代码引起的。你设计的实验,无论结果如何,你都能否定大约一半的可能性。不断重复,直到你找到错误的源头。

尽管名字很花哨,但这可以非常简单地完成。例如,我试图找到一个发生在某个特定文件中的错误。我可以使用注释或条件语句来阻止一半的文件执行。我还能重现这个错误吗?如果可以,那么我就知道这个错误是在活动的半个文件中。如果不是,那就一定是在非活动的那一半文件中。不管怎样,我已经排除了一半的可能性,并且更接近于找到错误的来源。

许多程序并不只是一个接一个的线性指令集。我们有条件反射和分支逻辑,导致流程看起来更像一棵树而不是一个列表。我们仍然可以使用二分搜索方法。与其使用 "将列表一分为二 "的心智模型,不如用修剪树上的分支的方式来思考,这样会有帮助。你可以消除的每一个分支都缩小了你需要继续搜索错误来源的区域。

二分搜索不仅可以让你在空间上找到bug,而且还可以在时间上找到bug! git bisect命令让你在你的git历史上使用同样的方法来有效地找到某个特定的bug是什么时候引入的。


演绎推理

这通常是人们在谈论 "逻辑 "或 "推理 "时想到的东西。我们在调试时一直在脑海中这样做。给出一系列开始的事实(称为前提),我们建立一个逻辑链来得出结论。这可能看起来像:

  1. 鉴于Postgres在验证唯一性约束时提出了一个重复键错误
  2. 考虑到违反唯一性约束只发生在INSERT或UPDATE语句上
  3. 鉴于唯一的数据库写入发生在CreateOrderService对象中
  4. 因此,错误一定发生在CreateOrderService对象中。

我们怎么知道呢?演绎推理是最数学化的推理形式,可以表示为一种等式:x ⇒ y,读作 “如果x则y”,或 “x意味着y”。

duplicate_error ⇒ index_violation
index_volation ⇒ db_write
db_write ⇒ create_order_invoked

有各种数学定律可以应用于这些 “逻辑方程”。有些只是感觉上的常识,其他的(如,德摩根定律)并不是立即就能直观感受到的。如果你想进一步挖掘这个话题,你需要搜索的术语是 “命题逻辑”。


这里特别令人感兴趣的是传递属性。它指出,一长串的如果…那么…那么,如x⇒y⇒z,可以简化为一个单一的如果…那么x⇒z,在我们上面的例子中,意味着:

duplicate_error ⇒ create_order_invoked

但要注意一些陷阱。如果你的任何起始前提是错误的,那么整个事情就会崩溃。在上面的例子中,如果还有其他文件也写到数据库中,那么我们的结论就可能是错误的。

一个更微妙的陷阱是,你的直觉可能导致你错误地应用一些规律。这方面一个特别常见的例子是把⇒当作是双向的关系。仅仅因为索引冲突意味着发生了数据库写入,并不意味着反过来也是如此。数据写入并不意味着发生了索引冲突。

在你的脑海中真的很容易犯这些错误。很多时候,从这种错误中恢复过来的最好方法是放慢脚步,写下你的前提和你如何得出结论的。了解一些命题逻辑符号会有帮助,但普通的散文也可以。


矛盾证明

也被称为 “归谬法”(reductio ad absurdum),来自一个拉丁短语,意思是 “归谬法”。这是演绎推理的一个变种,但你不是试图证明某件事情是真的,而是试图证明相反的错误。

这个过程通常是这样的:

选择一个你想证明为真的假设。现在,假设其反面为真。使用演绎推理,从你的相反假设中得出逻辑结论。得出一个不可能或荒谬的结论。

我们的目标是得出一个不可能是真的结论(矛盾)。因为它不可能是真的,所以你的相反假设也不可能是真的,因此,原来的假设一定是真的。用命题逻辑表示法:

P ⇒ Q
¬Q
∴ ¬P

这在实践中是什么样子的呢?

比方说,当你试图将坏数据从表单中保存到数据库中时遇到了一个错误。我们很难推理出错误发生在哪里,但很容易显示出错误没有发生的地方,这就以一种迂回的方式告诉我们错误发生在哪里。

你怀疑错误发生在数据库之外。矛盾推理将尝试遵循相反的推理(错误发生在数据库内),并证明这导致了荒谬的结果。

  1. 鉴于我们试图保存的数据与现有数据重复
  2. 鉴于我们的数据库有一个唯一索引
  3. 鉴于向数据库保存重复的数据会导致唯一索引异常
  4. 鉴于我们没有看到唯一索引异常
  5. 鉴于错误发生在数据库中(我们的相反结论)。
  6. 因此我们的数据库不强制执行唯一索引(矛盾)。

与演绎逻辑一样,如果你的任何前提是错误的,那么你的结论也可能是错误的。特别是,"这种情况不可能发生 "的假设往往是错误的。在上面的例子中,如果发现那个特定的数据库表没有唯一索引,我们的逻辑就会崩溃。糟糕!。


归纳推理

归纳推理是在我们看了一堆具体的例子并试图推导出一个更广泛的原则时使用的。在调试中经常是这样的情况。我们并不总是有一套 "真理 "来推理。相反,我们只是有:“在X情况下会发生这种奇怪的行为,但在Y情况下会发生不同的奇怪行为”。

更多的样本案例来推理是特别有用的。因此,我们尝试在本地重现这些问题。我们改变一些输入,看看这对结果有什么影响。我们做大量的笔记。我们甚至可以积累生产中的日志。然后我们尝试检测模式。

科学方法就属于这个类别,是归纳调试的好方法。17世纪科学的名称是 “自然哲学”,这不是没有道理的。一旦我们认为我们看到了一些模式,我们就试图证明我们的预感是错误的。我们可以通过做一个建议的修复,然后尝试手动重现这个错误来做到这一点。我们还可以添加一些自动化测试案例,检查一堆已知的触发问题的方式。如果我们的修改始终不能触发这个bug,那么我们的修复就很可能是正确的。

请注意,和科学一样,归纳推理并不能肯定地证明什么。相反,它是在我们能够接触到的情况下,对正确解决方案的最佳近似。有可能存在一些我们没有考虑到的边缘情况。有可能我们的 "修复 "只是掩盖了症状,但并没有解决根本问题。最终,我们可能会得到一些更多的表明我们错了的样本案例。

当我陷入困境时,我喜欢使用的归纳技巧是 “搅拌锅”,可以这么说。我向一段代码扔出一堆不同的数据,看看它是如何反应的。我甚至可能对代码本身做一些半随机的改变,看看结果会有什么变化。我们的目标并不是要找到一个解决方案,而是要产生一系列的样本案例,以便我有足够的数据点来开始看到模式。


谬误的谬误

随着我们对每一种推理类型的探索,总是有一些陷阱,推理不正确地把我们引向错误的道路。然而,仅仅因为你的推理有缺陷,并不一定意味着你的结论是错误的。这就是谬误的谬误。有时你很幸运。其他时候,你的直觉引导你找到了正确的解决方案,尽管你用来证明的逻辑是错误的。

始终验证你的假设、前提和结论!


总结

这里的推理形式并不是相互排斥的。在一个典型的调试过程中,你可能想同时使用所有这些方法。事实上,即使在阅读这篇文章时,你也可能认为所描述的一些技术来自其他章节的推理方法。你没有错。

我们每天在调试时都会凭直觉使用所有这些推理方法,甚至更多。然而,对我们正在使用的方法有一个明确的了解,可以让我们以一种更有条理的方式来解决这个问题,避免兜圈子。了解我们正在使用的技术的陷阱也使我们能够保持警惕,避免一些逻辑上的死胡同。

逻辑推理能力训练与程序调试相关推荐

  1. 如何使用日志进行程序调试_如何使用日志节省调试时间

    如何使用日志进行程序调试 by Maya Gilad 通过Maya Gilad 如何使用日志节省调试时间 (How to save hours of debugging with logs) A go ...

  2. 应用程序调试技术(更新程度:完毕)送源码及PPT

    课程简介 调试技术实务分为调试基本概念和设置,Windows下常用到调试工具VS和Windbg的使用,自动化调试技术,以及实际调试场景示例等.不仅仅是介绍枯燥的程序调试技术,而是深入的从操作系统实现以 ...

  3. Linux下的程序调试——GDB

    无论是多么优秀的程序员,都难以保证自己在编写代码时不会出现任何错误,因此调试是软件开发过程中的一个必不可少的 组成部分.当程序完成编译之后,它很可能无法正常运行,或者会彻底崩溃,或者不能实现预期的功能 ...

  4. 视唱练耳训练小程序开发,摆脱传统训练制约性

    视唱练耳作为一门综合性的音乐基础理论学科,对于声乐.器乐.舞蹈等音乐学科中的各个方面都起着十分重要的作用,尤其是突出表现在基本理论.基本技能和音乐审美上,对培养和发展学生的乐感.唱奏技巧以及音乐思维等 ...

  5. c语言常见错误分析和程序调试,C语言中常见错误分析及程序调试

    总结分析了C语言中常见错误及程序调试的相关技巧,以提高初学者的编程能力. 2 8 2月 0年l 0 电脑学习 第6 期 C语言中常见错误分析及程序调试 陈伟' 陈东淼 摘要:总结分析了 C语言中常见错 ...

  6. 从prolog到LTN,AI的逻辑推理能力1

    趁着假期快速阅读了一些关于逻辑编程的文献,喜欢上了逻辑编程这种编程方式. 然后,顺藤摸瓜,果然, 神经网络+逻辑编程 ,学术界的研究也有了初步的成果,例如Logic Tensor Networks . ...

  7. 考考你的逻辑推理能力

    自以为自己作为一名程序猿,逻辑推理能力会不错,不过昨天在一本叫做<你以为你以为的就是你以为的吗>书里做了四道测试题,大败啊. 现在把四道题给大家公布出来,考考各位程序员的逻辑思维能力. 一 ...

  8. ChatGPT和GPT-4的逻辑推理如何?浙大等最新《ChatGPT和GPT-4逻辑推理能力全面评测》论文解答,常规优异新数据差...

    来源:专知 运用逻辑推理能力进行全面的自然语言理解尝试.随着生成预训练Transformer 4(GPT-4)的发布,它在推理任务上被称为"先进"的,我们渴望了解GPT-4在各种逻 ...

  9. [转载]项目经理能力训练-如何讲解PPT?

    原文地址:项目经理能力训练-如何讲解PPT?作者:吴永达 准备: 1.了解听众背景(年龄.学历) 2.了解听众目前存在的问题(痛苦) 3.明确讲解的目标 策略: 1.产品介绍类:听众的问题1-造成的痛 ...

最新文章

  1. 零基础自学python教程-零基础人员可以学习python吗?|Python培训基础教程
  2. 一些安全相关的HTTP响应头
  3. 摘抄自知乎的redis相关
  4. 建立a8 linux开发环境,Fedora 14下建立 omap3530 开发环境 - 交叉编译器
  5. 【转载】shell实例手册
  6. 简化java_Java泛型太复杂了?如何简化?
  7. 7.15 HTMl + CSS 笔记整理(一)
  8. 【SaaS】企业微信裂变系统引流变现系统产品介绍
  9. 如何自动申请京东试用商品、签到获取京豆
  10. android 播放直播流,安卓大部分浏览器播放HLS协议直播流会从头开始
  11. NLP-文本摘要:“文本摘要”综述(Text Summarization)
  12. linux 串口 arduino,linux通过串口对arduino读写
  13. ArcGIS制图之阴影效果的表达与运用
  14. imprecise external abort
  15. C语言之父Dennis Ritchie辞世
  16. 小番茄(Visual Assist X)常用快捷键
  17. 密码学-编码算法:Base64编码原理和使用
  18. 全部SAP转储订单(STO)
  19. 没有钱该怎么创业?没钱创业依旧可以赚钱
  20. Spring Cloud Spring Cloud Alibaba 知识点总结

热门文章

  1. 微信小程序各错误码及原因自用表
  2. 威尔士和英格兰同属英国,但为啥还要在世界杯上进行PK?
  3. Linux中安装IDA
  4. 轻量级肝脏与肝脏瘤2.5D分割网络阅读笔记
  5. Quartus的SignalTap的使用
  6. 迷你播放器--第一阶段(2)--退出时自动最小化(不是关闭),增加当前播放曲目的跑马灯效果
  7. 风帆头,旗帜服,“背”在肩上的古国王印
  8. 每日一滴(实践)——NLP之处理停用词
  9. 最火后台管理系统 RuoYi 项目探秘,之二
  10. 电脑护眼设置---保护你的眼睛(转载)