unix编程哲学_编程哲学

unix编程哲学

by Haoxian Chen

陈浩先

编程哲学 (The philosophy of programming)

逻辑思维===好的软件 (Logical thinking === good software)

Programming is the new literacy. But how do we write good programs? Here are the recurrent questions we need to solve:

编程是新的素养。 但是我们如何编写好的程序? 以下是我们需要解决的常见问题:

  • How do we come up with algorithmic solutions to a problem?

    我们如何提出问题的算法解决方案?
  • Then, how do we know the solution actually works?

    然后,我们如何知道该解决方案实际起作用?
  • Even if we’re sure it works, how do we tell the computer to execute it?

    即使我们确定它可以工作,我们如何告诉计算机执行它?

Fun fact — if you have hard time grinding any of these questions, you are actually doing philosophy.

有趣的事实-如果您很难解决所有这些问题,那么您实际上是在做哲学。

To see why, let’s examine some interesting similarities between programming and philosophical reasoning.

要了解为什么,让我们研究一下编程和哲学推理之间的一些有趣相似之处。

第一条原则:您必须认真思考。 (The first principle: you have to think hard.)

Computers do nothing smarter than we can do — the difference is, they do it with faster speed.

计算机没有比我们能做的聪明的事-区别在于,它们以更快的速度来做。

But they cannot solve an actual problem like “how do I get to my office from home?”

但是他们无法解决诸如“我如何在家中到达办公室?”之类的实际问题。

An effective method can (in principle) be carried out by a human being unaided by any machinery except paper and pencil.
一种有效的方法(原则上)可以由除纸张和铅笔之外的任何机械辅助下的人员执行。

The Church-Turing Thesis

教会图论

The merit of programming still lies in the reasoning part. That is, translating a real world problem into simple instructions that a computer can execute.

编程的优点仍然在于推理部分。 也就是说,将现实世界中的问题转换为计算机可以执行的简单指令。

Of course, different programming languages have different levels of semantic abstractions. A Python program might be shorter than its C counterpart. But that just changes the amount of translations. We cannot get rid of the translation work. But we’ll leave this discussion for later.

当然,不同的编程语言具有不同级别的语义抽象。 Python程序可能比C程序短。 但这只会改变翻译量。 我们不能摆脱翻译工作。 但是我们将把讨论留给以后。

程序员的推理流程 (A programmer’s reasoning flow)

Now we are staring at some problem description. We may first look for small-scale input-output examples to understand the problem:

现在我们盯着一些问题的描述。 我们可能首先要寻找小规模的输入输出示例来理解该问题:

Induction. We need an algorithm that can handle such examples. Now you are doing induction: generalizing principles from experience.

感应。 我们需要一种可以处理此类示例的算法。 现在您正在做归纳法:从经验中总结原理。

Deduction. How do we know if it works for other unknown input? We do deduction to prove the correctness of our algorithm.

扣除。 我们如何知道它是否适用于其他未知输入? 我们做推论以证明我们算法的正确性。

Ontology. We have to maintain data in computer memory. The goal is to make them efficient for the computers to process. In order words, what data structure can best capture the dynamic flow of my information?

本体论 。 我们必须将数据保存在计算机内存中。 目的是使它们高效地用于计算机处理。 换句话说,哪种数据结构可以最好地捕获我的信息的动态流?

Induction again. Then comes the very final stage: debugging. We induce the buggy part of the program from analyzing the unexpected outputs.

再次感应 。 接下来是最后一个阶段:调试。 我们通过分析意外的输出来引导程序的错误部分。

一个例子 (An example)

Now let’s examine the above process by following this real example: finding the shortest path from vertex A to vertex E.

现在,让我们通过一个真实的例子来检查上述过程:找到从顶点A到顶点E的最短路径。

For small scale problems, we can solve them by instincts. Like, it’s very straightforward for us to recognize the solution A-C-E just by looking.

对于小规模的问题,我们可以凭直觉解决它们。 就像,对于我们而言,仅通过查看即可识别解决方案ACE非常简单。

But what about more complex topologies? What about different edge weights?

但是,更复杂的拓扑又如何呢? 那么不同的边缘权重呢?

We need help from computers. Yet it’s not straight forward to tell a computer what to do. There is a gap between how humans think and how a computer works.

我们需要计算机的帮助。 然而,告诉计算机怎么做并不是直接的。 人们的思维方式与计算机的工作方式之间存在差距。

过程 (The process)

1.总结经验:算法 (1. Generalize principles from experience: algorithms)

To tell a computer what to do, we need to first come up with an algorithmic procedure.

要告诉计算机该做什么,我们首先需要提出一个算法过程。

Greedy strategies are a natural way to proceed. That is, starting from the source vertex A, and going all the way along the known shortest path. Keep exploring new vertices until we reach destination E. And indeed, this approach satisfies our example.

贪婪的策略是自然而然的方法。 也就是说,从源顶点A开始,一直沿已知的最短路径进行。 继续探索新的顶点,直到到达目的地E。确实,这种方法满足了我们的示例。

The intuition is that, along the shortest path to a destination, every intermediate node is visited in the shortest path as well. (Of course this intuition assumes that all edges have positive weights. This may not hold true, depending on the applications. We will discuss this in detail later).

直觉是,沿着到达目的地的最短路径,每个中间节点也在最短路径中被访问。 (当然,这种直觉假定所有边缘都具有正权重。根据应用的不同,这可能不成立。我们将在后面详细讨论)。

So here is the algorithmic procedure:

所以这是算法过程:

  1. Some setup: (1) bookkeep the vertices we have visited: a set (visited), (2) remember the known shortest distances to each vertex that use only discovered vertices: another set distance(u). Every vertex’s distance is initially infinity, except for the source vertex is 0.

    一些设置:(1)预订我们访问过的顶点:一个集合( visited ),(2)记住到每个仅使用发现的顶点的已知最短距离:另一个集合distance(u) 。 每个顶点的距离最初都是无穷大,但源顶点为0。

  2. Now we start exploring: first we visit the vertex that has the known shortest path so far, say it’s u. (Initially it would be the source vertex).

    现在我们开始探索:首先,我们访问迄今为止已知的最短路径的顶点,说它是u 。 (最初它将是源顶点)。

  3. When standing at vertex u, we look around the out-going edges. Each edge, say(u,v), gives us a path to vertex v that uses vertex u as the last but only one step. If any of them is indeed a shorter path to v , or the first path we found to v, hooray we can update our set of shortest paths now. Otherwise ignore and keep going.

    当站在顶点u ,我们环顾外边。 每个边(例如(u,v)为我们提供了一条通往顶点v的路径,该路径使用顶点u作为最后但仅一步的步骤。 如果它们中的任何一个确实是通向v的更短路径,或者我们找到通向v的第一个路径,那么我们现在就可以更新最短路径集。 否则忽略并继续前进。

  4. We are done with vertex u, so we add it into our visited set.

    我们已经完成了顶点u ,所以我们将其添加到visited集中。

  5. Go back to step 2, keep exploring until we have visited all vertices.

    返回步骤2,继续探索,直到我们访问了所有顶点。

distance can now tell us the global shortest distances, because it’s used to keep the shortest distances using only visited nodes. And all vertices are visited when the algorithm finishes.

distance现在可以告诉我们全球最短距离,因为它用于仅使用访问的节点来保持最短距离。 算法完成后,所有顶点都将被访问。

We just reinvented Dijkstra’s algorithm. Of course, there are many other algorithms for finding the shortest path. But the point is, we need a algorithmic procedure to solve the problem.

我们只是重新发明了Dijkstra的算法。 当然,还有许多其他算法可以找到最短路径。 但是关键是,我们需要一种算法程序来解决该问题。

2.通过推论验证一般原则 (2. Validate general principles by deduction)

How do we make sure the algorithm’s principles are correct? We can either increase our confidence by testing the principle against more input examples, or, more effectively, we can find a rigorous mathematical proof.

我们如何确保算法原理正确? 我们可以通过对更多输入示例进行测试来提高我们的信心,或者更有效地,我们可以找到严格的数学证明。

Like an a priori proposition in philosophy, the correctness of an algorithm is independent of its execution. In other words, testing cannot guarantee the correctness of algorithms. We need to prove it.

就像哲学中的先验命题一样,算法的正确性与它的执行无关。 换句话说,测试不能保证算法的正确性。 我们需要证明这一点。

Here’s the basic flow of the proof:

这是证明的基本流程:

1. For all visited vertices, we find the shortest paths.

1.对于所有访问的顶点,我们找到最短的路径。

2. The destination is visited.

2.访问目的地。

3. Therefore, we find the shortest path to the destination.

3.因此,我们找到了到达目的地的最短路径。

Don’t they seem somewhat familiar? Like this:

他们看起来不熟悉吗? 像这样:

1. All men are mortal.

1.所有的人都是凡人。

2. Socrate is a man.

2. Socrate是一个男人。

3. Therefore, Socrate is mortal.

3.因此,Socrate是凡人。

In fact, this is Syllogism, a classic form of deductive reasoning. This is pretty much what logicians are doing.

实际上,这就是三段论 ,一种演绎推理的经典形式。 这几乎就是逻辑学家正在做的事情。

Another interesting historical fact: the formal concept of computation was first come up by logicians in 1930s. They needed to know if certain logical problems are actually solvable at all (so they could avoid wasting their time solving something unsolvable). To answer that, they come up with the notion of computability.

另一个有趣的历史事实:1930年代,逻辑学家首先提出了计算的形式概念。 他们需要知道某些逻辑问题是否真的可以解决(这样可以避免浪费时间解决一些无法解决的问题)。 为了回答这个问题,他们提出了可计算性的概念。

Later in 1936, Alonzo Church and Alan Turing developed the formal definition of Computability, independently, at the same time. This paper gives a historical review of computation.

1936年下半年,Alonzo Church和Alan Turing同时开发了可计算性的正式定义。 本文对计算进行了历史回顾。

The conclusion’s correctness depends on the first two premises. In our proof, the second premise is trivial, since our algorithm is literally visiting all nodes. Yet proving the first premise, that we find the shortest path by the time we visit a node, needs some work.

结论的正确性取决于前两个前提。 在我们的证明中,第二个前提很简单,因为我们的算法实际上访问了所有节点。 然而,要证明第一个前提(即在访问节点时找到最短路径),还需要一些工作。

Mathematical induction can help. It is actually one of the most useful techniques to prove the correctness of a lot of other algorithms.

数学归纳法会有所帮助。 实际上,它是证明许多其他算法正确性的最有用技术之一。

The general flow goes as follows. First, if an algorithm works on input 0, and second, if the fact that it works on input n implies that it works on input n+1 as well, then it works for all input greater or equal to 0. At this point you are able to guarantee the correctness of your algorithm.

总体流程如下。 首先,如果算法对输入0起作用,其次,如果算法对输入n起作用的事实意味着它也对输入n+1起作用,那么它对大于或等于0所有输入都起作用。 此时,您可以保证算法的正确性。

For simplicity, I’ll refer you to this lecture note for the complete proof of this path finding algorithm.

为简单起见,我将向您介绍本讲义 ,以获取此路径查找算法的完整证明。

Note that we should not confuse mathematical induction and philosophical induction. By the philosophical definition, mathematical induction is a deductive reasoning process, because it’s correctness is contained in itself, without any external observations.

注意,我们不应混淆数学归纳法和哲学归纳法。 按照哲学的定义,数学归纳是一个演绎推理过程,因为它的正确性本身就包含在内,而没有任何外部观察。

The lesson is: when we come up with an algorithm, it should be able to handle all possible execution cases.

教训是:当我们提出一种算法时,它应该能够处理所有可能的执行情况。

In practice, going through the rigorous mathematical proof may not be the most efficient strategy. But at least we want to consider as many execution cases as possible, especially the adversarial ones. This practice would improve the robustness of our code.

实际上,经过严格的数学证明可能不是最有效的策略。 但是至少我们要考虑尽可能多的执行情况,尤其是对抗性情况。 这种做法将提高我们代码的健壮性。

You may have noticed that, in our example path finding algorithm, it doesn’t work if the edge weight is negative. You can find the reason in this lecture note. To handle a negative-weight graph, you can use the Bellman-Ford algorithm.

您可能已经注意到,在示例路径查找算法中,如果边缘权重为负,则该算法无效。 您可以在本讲义中找到原因。 要处理负权图,可以使用Bellman-Ford算法

3.本体问题:数据结构 (3. The ontology problem: data structure)

So far we convinced ourselves that we have a correct algorithm. But this is only half the battle.

到目前为止,我们已经确信自己拥有正确的算法。 但这只是成功的一半。

The next question is, how do we feed the information into computers? Humans like visualized information, like graphs, or histograms. But today’s computers only deal with binary bits.

下一个问题是,我们如何将信息输入计算机? 人类喜欢可视化的信息,例如图形或直方图。 但是今天的计算机只处理二进制位。

We need to come up with a data structure that contains the essential information. And it should be efficient for a program to process at the same time.

我们需要提出一个包含基本信息的数据结构。 而且程序在同一时间处理应该是高效的。

Let’s continue on our path finding example. A path is an ordered list. But it’s irritating to deal with, compared to an integer. Note that in our algorithm we have to find the shortest path from our set of discovered paths. And then iterate all the way to its end. Seems like we have to dedicate an array or memory to store each path.

让我们继续寻找路径的例子。 路径是有序列表。 但是,与整数相比,这令人讨厌。 请注意,在我们的算法中,我们必须从发现的路径集中找到最短的路径。 然后一直循环到最后。 似乎我们必须专用于数组或内存来存储每个路径。

Could we do better? Note that in a valid path, consecutive appearances of elements must correspond to an edge in the graph. But, we already have the edge information and they are the same for all paths. Why can’t we get rid of this redundant information?

我们可以做得更好吗? 请注意,在有效路径中,元素的连续出现必须与图中的边相对应。 但是,我们已经有了边缘信息,并且所有路径的边缘信息都是相同的。 为什么我们不能摆脱这些多余的信息?

Well, we can get rid of the list. It turns out that in order to gather the shortest path, the intermediate step is to determine what is the next hop you need to go. All we need to retrieve the shortest path from source A to any target node is just the graph itself, and the shortest distance from A to every node.

好吧,我们可以摆脱这个清单。 事实证明,为了收集最短路径,中间步骤是确定您需要经过的下一跳。 我们需要检索从源A到任何目标节点的最短路径的只是图本身,而从A到每个节点的最短距离。

A visual representation is in the above picture. This representation is memory efficient. It’s also more efficient for the program to process.

上图中是视觉表示。 此表示具有存储效率。 该程序的处理效率也更高。

This is how it constructs the shortest path using only the distance vector. Start from the destination vertex, and an empty path. Look up intermediate vertices through incoming edges. Pick the one with the lowest value in distance. Add it to the head of the path. Repeat until we reach the source. This trick actually has a formal notation, called back-tracking.

这就是它仅使用距离向量构造最短路径的方式。 从目标顶点开始,然后是一个空路径。 通过传入边缘查找中间顶点。 选择distance最小的那个。 将其添加到路径的开头。 重复直到我们到达源头。 这个技巧实际上有一个正式的表示法,称为回溯

Philosophers seek for the eternal truth. And programmers want to find out the precise data structure that best captures the dynamics of information. As you see in the path finding example, all it needs to give a shortest path is just a vector, telling you the shortest distances to each vertex. This holds true for any graph, regardless of the number of vertices or edges.

哲学家寻求永恒的真理。 程序员希望找到最能捕获信息动态的精确数据结构。 如您在路径查找示例中所看到的,给出最短路径所需的只是一个向量,它告诉您到每个顶点的最短距离。 无论顶点或边的数量如何,这对于任何图形都适用。

4.后验命题:调试 (4. A posteriori proposition: debugging)

Most programmers have gone through this reasoning tons of times. I bet this is one of the most difficult and time-consuming part of any programming task.

大多数程序员经历了无数次这种推理。 我敢打赌,这是所有编程任务中最困难,最耗时的部分之一。

For example, segmentation faults in C programs are often caused by null pointer references. I learned this after dealing with tons of C/C++ segmentation faults. Of course, there are more subtle cases that relate to personal programming habits.

例如,C程序中的分段错误通常是由空指针引用引起的。 在处理大量的C / C ++分段错误之后,我学到了这一点。 当然,还有更多与个人编程习惯有关的微妙情况。

The following example is a syntax mistake made by a programmer. The if condition should have been is_float==1 , but the programmer mistook the logical equal operator == as an evaluation operator =. This will still pass the compiler’s check, because either is correct syntax.

以下示例是程序员犯的语法错误。 if条件应该是is_float==1 ,但是程序员将逻辑相等运算符==误认为是评估运算符= 。 这仍然会通过编译器的检查,因为两者都是正确的语法。

if (is_float = 1) {  // do something}

This is an inductive reasoning process. Your debugging diagnosis only makes sense if you have observed enough program executions.

这是一个归纳推理过程。 仅当您观察到足够的程序执行量时,调试诊断才有意义。

Here is where the value of practice comes in. No matter what kind of techniques you are learning, you have to gather enough practical data. Otherwise, you wouldn’t have enough experience to conduct induction.

这就是实践的价值所在。无论您正在学习哪种技术,都必须收集足够的实践数据。 否则,您将没有足够的经验进行归纳。

You should keep an eye on the recurrent patterns in your buggy codes. When you find a bug, fixing it is not enough. You need some extra cause-effect analysis on your own programming practice. Ask yourself: is this part of my programming habits particularly vulnerable to these kinds of bug?

您应该注意越野车代码中的重复模式。 当您发现错误时,仅对其进行修复是不够的。 您需要在自己的编程实践中进行一些额外的因果分析。 问问自己:我的编程习惯的这一部分是否特别容易受到此类错误的影响?

那为什么重要呢? (So why does it matter?)

Programming is not just about writing code, it’s a systematical way of reasoning. Because code, or instructions, is just a means to an end. With the development of program synthesis technique, you may not even bother writing code and debugging yourself. All that matters is if you can tell the computer what to do.

编程不仅与编写代码有关,而且是一种系统的推理方式。 因为代码或指令只是达到目的的一种手段。 随着程序综合技术的发展,您甚至不必费心编写代码和调试自己。 重要的是您是否可以告诉计算机该怎么做。

As the first step towards improving your programming skills, this article reveals the underlying reasoning pattern that we may not even recognize when we were programming. Most of us rely on subconscious, automatic reflection to finish most of our day-to-day tasks. But where does improvement come from? It first comes from noticing some fallacy or mistakes we made in our reasoning process.

作为提高您的编程技能的第一步,本文揭示了我们在编程时甚至可能不认识的潜在推理模式。 我们大多数人依靠潜意识的自动反射来完成我们的大部分日常任务。 但是,改进从何而来? 首先来自注意到我们在推理过程中犯的一些谬误或错误。

For practical advice, I recommend this article on how to think like a programmer, and this book on the same topic but with more details.

要获得实用建议,我建议您阅读这篇有关如何像程序员一样思考的文章,并推荐有关同一主题但有更多细节的本书

参考文献 (References)

翻译自: https://www.freecodecamp.org/news/the-philosophy-of-programming-e901bd37363a/

unix编程哲学

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值