编程的异想世界

现在网络上的编程课程一浪接着一浪,什么少儿编程,成人编程,AI编程,层出不穷。

比如朋友圈火过一阵的Python编程,好像不学点Python都不要意思在职场混。虽然现在都喜欢打着Python的旗号来吸引大家学编程,但是笔者想表达一个观点:

编程不仅仅是学习一门编程语言

可以从编程的发展来论证这个观点,说到编程,就要讲到计算机。

编程与计算机

计算机的产生是用来解决数学问题,数学问题可以是逻辑问题也可以是计算问题。

最早的电子计算机用于弹道计算。现在对电子计算机的定义是:利用数字电子技术,根据一系列指令指示并且自动执行任意算术或逻辑操作串行的设备。这个定义通俗的讲,就是可以运来解决一些数学问题,比如求解二元一次方程,或者逻辑问题,比如你妈和你女朋友同时落水,你会先救哪一个。

电子计算机有四个主要部分:算术逻辑单元、控制电路、存储器和输入输出设备。目前算术逻辑单元和控制电路被集成到了一块电路上,就是CPU。CPU就是计算机实际执行指令的地方,计算机的运行过程大致可以描绘成先从存储器中获取指令和数据,然后执行指令,存储数据,再获取下一条指令。这个过程被反复执行,直至得到一个终止指令。

指令如同数据一样在计算机内部是以二进制来表示的,比如说,10110000就是一条Intel x86系列微处理器的拷贝指令代码。我们把一连串的指令叫做程序。

为了使计算机能够理解人的意图,我们就必须将需解决的问题的思路、方法和手段通过计算机能够理解的形式告诉计算机,使得计算机能够根据人的指令一步一步去工作,完成某种特定的任务。这种人和计算体系之间交流的过程就是编程。

编程就是编写完成某个特定任务的一连串指令。

现在我们使用的手机、笔记本、平板其实都是电子计算机,都拥有前面讲的4个部分。

编程与编程语言

计算机能理解的语言叫做机器语言,也叫指令。这些指令是以二进制表示的,我们识别和操作比较困难,所以一开始发明了汇编语言,把二进制的指令变成一些助记符,利用这些助记符来进行指令的编写。比如我们编写一个输入两个数,输出两个数相加结果的程序,我们先使用汇编语言来进行编写:

data1 segment
    var db 6,0,6 dup(0),'$'
    da1 db 2 dup(0),0ah,0dh,'$'
    da2 db 2 dup(0),0ah,0dh,'$'
    str1 db 'please input two variable!',0ah,0dh,'$'
data1 ends

code1 segment
    assume ds:data1,cs:code1
    start:
            mov ax,data1; 初始化
            mov ds,ax
            mov dx,offset var; 输入相加的数
            mov ah,0ah
            int 21h

            mov si,2    ;把第一个数移动到大da1
            mov cx,2
            lea di,da1
    loopda1:mov bl,byte ptr var[si]
            mov byte ptr [di],bl
            inc si
            inc di
            loopnz loopda1

            inc si
            mov cx,2
            lea di,da2
    loopda2:mov bl,byte ptr var[si]
            mov byte ptr [di],bl
           inc di
           inc si
           loopnz loopda2   

        lea di,da1
        lea si,da2

        mov cx,2

  loop1:sub byte ptr [di],30h
        sub byte ptr [si],30h
        inc di
        inc si
        loopnz loop1

        dec si
        dec di
        clc
        mov cx,2
        mov bp,0
  loop2:mov al,[di]
        adc al,[si]
        aaa
        mov [di],al
        dec di
        dec si
        loopnz loop2

        inc di
        mov cx,2
    loop3:
        add byte ptr[di],30h
        inc di
        loopnz loop3

        mov [var+4],'+'
        mov [var+7],'='
        mov dx,offset var
        add dx,2
        mov ah,09h
        int 21h
        mov dx,offset da1
        mov ah,09h
        int 21h
        mov ah,4ch
        int 21h
code1 ends
end star
————————————————
版权声明:本文为CSDN博主「loudyten」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/loudyten/article/details/8222373

隐约能看到add这个单词,表示是相加操作,上面这一段指令包含了很多直接操作CPU的指令,基本上描述了计算机的整个运行细节。但是人们觉得像这样根据机器语言进行编程,效率太低,而且容易出错。就希望能想平时说话写字,解答数学题目一样编程,有了这个目标,很多编程语言就被发明了出来(相对于汇编语言,这些语言可以统称为高级语言),比如C语言,用C语言实现两个数相加

#include 

int main()
{
    int a,b;
    scanf("%d %d",&a, &b);
    printf("%d\n",a+b);
    return 0;
}

是不是比上面的汇编简单了不止10倍,但是用高级语言编写的指令是没法直接让计算机理解了,这个时候需要一个翻译,把高级语言翻译成计算机能认识的机器语言,计算机才能运行。这就是每个高级语言都会带一个自己的编译器(C语言)或者是解析器(Python语言)。编译器和解析器都是用来将高级语言转化为机器语言的。

不同的任务可能需要不同的编程语言,比如制作网页结构需要HTML,编写安卓应用需要Java等等。但是语言只是工具,用工具的人才决定了产出的价值。

编程与应用程序

我们平时使用手机、电脑。其实是使用上面的应用程序,不管是打游戏,看视频,处理文档。我们都需要相应的应用程序,这些应用程序提供了我们想要的功能或者信息。而且这些应用程序都充分使用了计算机的输入输出设备,让我们可以和应用程序提供的信息进行交互。

要编写一个完成特定任务的应用程序,需要知道完成这个任务的每一步,然后把每一步转换成机器能理解的指令。这里简单来看有两个步骤:做什么和如何做。做什么编程这件事关不了,但是如何做就是编程需要操心的。

解决同一个问题,可能有很多种方法,编程的人首选需要找到解决这个问题的方法并且把它用某种编程语言进行实现。然后就有了这个应用程序。再抽象一点就是制定方案+实施方案。比如我们需要为编写一个程序解决下面这个问题:

你正在安装一个广告牌,并希望它高度最大。这块广告牌将有两个钢制支架,两边各一个。每个钢支架的高度必须相等。

你有一堆可以焊接在一起的钢筋 rods。举个例子,如果钢筋的长度为 1、2 和 3,则可以将它们焊接在一起形成长度为 6 的支架。

返回广告牌的最大可能安装高度。如果没法安装广告牌,请返回 0。

示例 1:

输入:[1,2,3,6]
输出:6
解释:我们有两个不相交的子集 {1,2,3} 和 {6},它们具有相同的和 sum = 6。
示例 2:

输入:[1,2,3,4,5,6]
输出:10
解释:我们有两个不相交的子集 {2,3,5} 和 {4,6},它们具有相同的和 sum = 10。
示例 3:

输入:[1,2]
输出:0
解释:没法安装广告牌,所以返回 0。

提示:

0 <= rods.length <= 20
1 <= rods[i] <= 1000
钢筋的长度总和最多为 5000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/tallest-billboard
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这个问题,数据大一点,人解决起来都很困难,更别说编程了。这就需要能想到一个能使用计算机语言进行表达的方案。一旦你想到了一个方案,使用什么编程语言来解决,就非常简单了。

下面来看一下使用一个方案,但是两种语言来实现的情况:

想法

对于每一根钢筋 x,我们会写下 +x,-x 或者 0。我们的目标是最终得到结果 0 并让正数之和最大。我们记所有写下的正数之和为 score。例如,+1 +2 +3 -6 的 score 为 6。

因为 sum(rods) 的大小限制,就说明可以利用这个性质。事实上,如果之前已经写下了一些数字,那么就不需要考虑这些数字是如何得到的。例如,rods = [1, 2, 2, 3],我们可以用 3 种方法得到和为 3,但只考虑最终的 score 为 3。数字之和的上界是 10001,因为只有 [-5000, 5000] 区间内的整数是可能的值。

算法

dp[i][s] 表示当我们可以使用 rods[j] (j >= i) 时能得到的最大 score,由于之前写下的数字和为 s(不统计在 score 内)。例如,rods = [1, 2, 3, 6],可以有 dp[1][1] = 5,在写下 1 之后,可以写下 +2 +3 -6 使得剩下的 rods[i:] 获得 score 为 5。

边界情况:dp[rods.length][s] 是 0 当 s == 0,剩余情况为 -infinity 。递推式为 dp[i][s] = max(dp[i+1][s], dp[i+1][s-rods[i]], rods[i] + dp[i+1][s+rods[i]])。

作者:LeetCode
链接:https://leetcode-cn.com/problems/tallest-billboard/solution/zui-gao-de-yan-gao-pai-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Java语言实现

class Solution {
    int NINF = Integer.MIN_VALUE / 3;
    Integer[][] memo;
    public int tallestBillboard(int[] rods) {
        int N = rods.length;
        // "memo[n][x]" will be stored at memo[n][5000+x]
        // Using Integer for default value null
        memo = new Integer[N][10001];
        return (int) dp(rods, 0, 5000);
    }

    public int dp(int[] rods, int i, int s) {
        if (i == rods.length) {
            return s == 5000 ? 0 : NINF;
        } else if (memo[i][s] != null) {
            return memo[i][s];
        } else {
            int ans = dp(rods, i+1, s);
            ans = Math.max(ans, dp(rods, i+1, s-rods[i]));
            ans = Math.max(ans, rods[i] + dp(rods, i+1, s+rods[i]));
            memo[i][s] = ans;
            return ans;
        }
    }
}

作者:LeetCode
链接:https://leetcode-cn.com/problems/tallest-billboard/solution/zui-gao-de-yan-gao-pai-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Python语言实现

from functools import lru_cache
class Solution:
    def tallestBillboard(self, rods):
        @lru_cache(None)
        def dp(i, s):
            if i == len(rods):
                return 0 if s == 0 else float('-inf')
            return max(dp(i + 1, s),
                       dp(i + 1, s - rods[i]),
                       dp(i + 1, s + rods[i]) + rods[i])

        return dp(0, 0)

作者:LeetCode
链接:https://leetcode-cn.com/problems/tallest-billboard/solution/zui-gao-de-yan-gao-pai-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

通过这个问题,想说明的是,编程包括了两个步骤:实现方案和把实现方用编程语言表达出来。

上面这个例子虽然是解决一个算术问题,但是到开发一个实际的应用程序原理是一样的。

编程与电影

如果把编程比作拍电影,那么剧本就相当于实现方案,拍摄器材相当于编程语言,演员相当于使用的开发技术,比如框架,第三方类库等(不是从头开始培养演员,而是有很多现成的演员可以拍这个电影),导演就是编程的人。

要是这样类比就能理解,会使用拍摄器材就能拍出小视频,能请到明星,拍出的东西肯定有流量,如果剧本好,群众演员也能拍出好的电影。但是同一个故事,不同的导演往往能拍出不同的水平,这其中的奥秘不是器材、演员和剧本能决定的。

从学好拍电影的角度讲,学习编程你可能需要知道如何使用开发技术,如何选取适当的编程语言,理清楚实现方案(可以从最简单的两个数相加问题开始)然后不断拍小视频检验自己的作品,能否算的上是一个好的短片(程序)。

所以编程其实不仅仅是学一门编程语言那么简单,语言只是解决了拍摄问题,能帮你处理远景,人物,调色等等问题,但是要拍什么,拍成什么样,表达什么核心观点,才是导演重点思考的问题,不然可能只是一名优秀的摄像师。

欢迎大家讨论。

你可能感兴趣的:(编程的异想世界)