首页 PHP学习教程

翻译专业学生的第一节编程入门课(PHP版)

From:

前言


好友魏勇鹏希望我给翻译专业的同学做一次译者编程入门的讲座,这让我想起十年前我刚来北京读研究生时在他公司实习的经历。2011年10月4日,我第一次到清华科技园,第一次和程序员一起共事。


那时我刚刚开始从英语专业本科生转化为半文半工的研究生,脑子里还都是文科那套思考世界的方式。


接下来的几年,我一面在学校里接受着批判性思维、面向对象的编程思想、逻辑思维、项目管理思维、产品思维的训练,一面在勇鹏的语智云帆公司以产品实习生的身份参与计算机辅助翻译工具的设计和运维,渐渐对编程产出了浓厚的兴趣,希望自己也能够用代码写出自己的翻译小工具。


再往后,2013-2014年,我在做毕业论文的时候,开始尝试开发简易的翻译作业批改工具。


2014年到北语高翻开始工作后,我想带着学生一起摸索如何开发计算机辅助翻译工具,但是市面上能找到的开源工具只有OmegaT,还是用Java来写的,于是我就开始解密一般,一点点寻找计算机辅助翻译工具核心技术的代码实现方法。


我记不起当时为什么会选择PHP,而不是Python,反正就这么开始自己写代码,先后实现了术语库、翻译记忆库、术语抽取、术语匹配、编辑距离计算、机器翻译API调用等功能的实现,并将许多代码分享到了简言微信公众号上,并将其中的精华部分又梳理成册,从2017年到2020年用了将近三年的时间完成了《译者编程入门指南》一书。这学期我又将偏计算机辅助翻译工具实现原理的部分重新梳理成了一门本科生的专业课《翻译技术原理与实践》,目标就是带着学生实现简易在线计算机辅助翻译工具的开发。


现在,我自己能够用PHP、JS等编程语言开发笔译教学平台、机器翻译评测工具等功能相对复杂的小系统,能够用Python做一些小工具和数据统计分析工作。


比如ParaTrans:


输入一段原文可以同时获得四个机器翻译的结果,并通过回译来评估翻译质量。


比如LingualJob:



这是一个可以布置和批改翻译作业的平台,学生也可以上传简历寻找实习全职工作。


回过头去看自己学习编程的整个经过,再站在今天这个时间点思考如何帮助翻译专业的学生入门编程,如果只给我一个小时的时间,我大概会从这里开始讲起:


一、示例代码与编程环境


去年我的学生问了我一个问题:



很多人会问译者为什么要学编程,我想这就是一个非常典型的例子,这就是为什么我们要学习编程:我们在翻译实践中会遇到一些真实的需求,现有的工具无法解决问题,但我们自己写段代码就可以实现。


为了解决这位同学的问题,我做了这么一个工具:


http://translation.education/termcount/


截图如下:


使用方法很简单,就是把待翻译的原文粘贴到上面,然后再导入一个简单的Excel表格,如下:



运行后效果如下:


在上图中可以看到,术语表中的每一条术语在原文中出现的次数都标了出来,点击“高亮”按钮后还可以在右侧清晰看到原文中术语出现的位置。


那么这么一个小工具怎么才能开发出来呢?在译者编程入门讲座的第一课里,我无法将整个流程都介绍,但是我可以分享这么一段代码:



$terms = array("北京语言大学","北京外国语大学","翻译");
$text = "北京大学、北京外国语大学、北京语言大学都是开设有翻译专业的高等学府,培养了许多优秀的翻译人才。";
foreach($terms as $term){ echo $term.":".substr_count($text, $term).";";}


里面包含了几个非常关键的编程知识点:变量、数组、字符串、循环、函数。


但第一次接触到这段代码的同学并不知道这些知识点都体现在代码中的什么位置,也不知道怎么运行这段代码,更不知道如何自己修改这段代码来解决自己的具体问题。


所以可以先找到一个可以在线运行代码的网站运行看一看,了解一下这段代码的作用,比如:


https://c.runoob.com/compile/1



接下来将示例代码粘贴到上述网站的左侧,替换掉第二行,保留第一行和第三行:



接下来单机“点击运行”,看到如下结果:



由此可以看出,这段代码的作用是为了查看一段文本中几个名词出现的次数,直白一点就是,通过这段代码我们知道了:


“北京大学、北京外国语大学、北京语言大学都是开设有翻译专业的高等学府,培养了许多优秀的翻译人才。”这段文本中,北京语言大学出现了1次,北京外国语大学出现了1次,翻译出现了2次。


许多同学如果亲自运行了这段代码并成功看到了上图右侧显示的结果,可能会有一些疑惑:


1)这段代码是怎么写出来的?

2)代码中怎么那么多标点符号,比如:$、=、()、{}、.、;、

3)代码中怎么会有terms、array、text、as、echo这些似曾相识的英文单词,也怎么会有foreach、substr_count这样没怎么见过的英文单词?

4)代码最上面怎么会有<?php这样的标点符号和英文组合,代码最下面怎么会有?>这样的标点符号组合?


这些是我们初次看到程序语言时非常正常的想法。


为了产生和大家类似的疑问,我去找了一段我也没有学过的编程语言:Go语言


package main
import "fmt"
func main() { for true { fmt.Printf("这是无限循环。\n"); }}


看到这段语言时我大概能猜出来每一行大概是在干什么,但我并不知道通过怎样的方式我才能在自己的电脑上运行这段代码,也不知道假如我想稍微修改一下这段代码实现某个我想实现的功能应当如何操作。


我之所以会做这个比喻,就是因为以前我在教别人英语时会同时去学某个之前没有接触过的语言,比如意大利语、阿拉伯语,这样我可以站在学习者的角度来审视学习者接触新知识的过程。


上面这个例子让我意识到:


1)我需要有人指导我如何搭建编程环境

2)我需要有人告诉我编程环境搭建好之后如何运行示例代码

3)我需要有人告诉我示例代码是怎么写出来的,背后的基础知识是什么


这就是我想教给编程入门学习者的。


以上面的第一段代码为例,除了可以直接打开一个在线网站运行PHP代码,也可以使用更为专业的方法:安装编程环境、使用编辑器。


在国内,如果你使用的Windows电脑,可以前往这个网址:


https://www.xp.cn/


安装小皮面板,这个工具对中国的PHP编程学习者非常友好。


如果你使用的是Mac电脑,可以下载XAMPP或MAMP Pro。


但这个安装编程环境的过程一定要有人指导,否则你会还没入门就放弃。因此一定要有一次线下编程入门课,在别人指导下安装编程环境。


除了编程环境外,编辑器也很重要,这里可以考虑微软推出的Visual Studio Code:


https://code.visualstudio.com/


都安装完成后,找到根目录htdocs,并在其中创建一个空白文件夹test,并在test文件夹里创建空白文件index.php,并在这个文件里输入第一段代码,如下图:


接下来运行这段代码:


当你看到这里时,我相信你肯定已经放弃了。因为:


0)我刚想跟着你一起练习,但我的编程环境还没有装好呢......

1)去哪里找所谓的根目录htdocs?啥是根目录?啥是htdocs?

2)怎么创建空白文件index.php?为什么要创建这个空白文件?我创建了一个index.php.txt文件,这个可以吗?

3)如何把代码粘贴到那个网站中,粘贴进去之后怎么才算粘贴成功?

4)什么叫运行这段代码?你那个浏览器的界面是怎么显示出来的?


这一连串的问题呈现出许多编程学习道路上看似简单但又个个诛心的难题。许多编程教程默认我们懂得了示例代码操作的过程,但我们实际上并不知道。


所以啊,编程的学习是需要积累基础知识的,而针对文科生的编程知识讲解有时候也需要另辟蹊径。


二、几个编程的基础知识


讲完上面这个过程很有可能半个小时已经过去了,但是你似乎并不觉得自己学了编程,而是学了怎么为了学编程而在自己电脑上成功安装自己完全不了解的几个软件。


这就是人生,就像爬山一样,刚爬几步时并不觉得自己在爬山,可爬到山顶时才会有一览众山小的感觉。


还是看这段代码:


<?php
$terms = array("北京语言大学","北京外国语大学","翻译");
$text = "北京大学、北京外国语大学、北京语言大学都是开设有翻译专业的高等学府,培养了许多优秀的翻译人才。";
foreach($terms as $term){ echo $term.":".substr_count($text, $term).";";}
?>


因为我教PHP,所以一看就知道这段代码在干什么,但你第一次接触,并不晓得每一行都在做什么。


你不妨做这样一件事情:将上面这段代码大声朗读出来,不要漏掉其中的任何一个标点符号和中英文词。


读完之后你可能会产生一种非常异样的感觉,这种异样的感觉来源于信息的不对等,来源于我们无法对几行代码蕴含的信息进行解码。


比如:


1)为什么第一行是一个小于号(<)、一个问号(?)和三个字母(PHP)?

2)你会不会把$terms误以为是S开头的?$和S有啥区别?

3)array是什么,后面为什么有圆括号?

4)foreach是什么意思?

5)echo是什么意思?

6)第9行怎么会把句号放到句子里面,而不是句子末尾?

7)substr_count我只认识count,其他的是什么意思?


大家肯定还会有其他疑惑,那么是什么让我们无法理解这段代码呢?


我想是编程基础知识。


1)变量


$terms = array("北京语言大学","北京外国语大学","翻译");


在第一行代码中,我们首先看到的是$terms,我们称这个为“变量”(Variable),而变量就像粉笔盒一样,里面可以装东西:



这个时候你也许很好奇:不就是几个字符吗?“$”是个什么字符?“terms”也不过是五个字母,怎么就能像粉笔盒这种实体容器一样,用来装东西呢?


其实啊,这里的“$terms”是个名字,我们称其为“变量名”,它是变量的名字,而变量则是一个抽象的概念,我们看不到,代表的是计算机内部的数据存储空间,我们给这个空间起个名字来指代它。


在PHP中,我们会在变量名前使用“$”(Dollar Sign),所以看到$terms就知道这是一个变量名。


但是在其他编程语言中并非如此,像Python这样的编程语言,变量名就是一个普通的字符串,比如:


#!/usr/bin/python3 terms = '北京语言大学'


一对比就会发现,PHP的变量名要多个“$”,Python的则没有这种标识符。


除此以外,大家也会发现,我们在变量名后面用了等号“=”:


$terms = array("北京语言大学","北京外国语大学","翻译");


这个等号容易被误以为是“1+1 = 2”中的等号,它们的作用并不一样。在“1+1=2”中的等号是用来表示两边的数值运算结果是相等的,而上面这段代码中的等号则是“放置”的作用:将符号右边的数据放置到变量名指代的那个计算机内部的空间里。但如果每次都这么读这么记很不方便,也不规范,所以我们可以用“赋值”这个说法:定义一个变量$terms,并将右边的数据赋值给这个变量。


在我们这段代码中,大家可以看到好几个变量:


$terms、$text、$term


他们里面都是装载着数据的,只是有时候我们知道这个数据具体是什么,有时候这个数据是变化的,所以我们称其为:变量,变化的量。


2)数组和字符串


变量实际上是一个容器,至于容器里能装什么,还得看我们把什么装了进去。


在这行代码中,我们装了三个术语进去,并且用array()将三个术语包裹住:


$terms = array("北京语言大学","北京外国语大学","翻译");


这时候我们放置到变量里的数据类型是数组(array),可以理解为:数据的组合。我们将三条术语视为一个组合,把他们变成一个组合的工具就是array(),我们其实可以将其视为一个动词(Verb),正是因为这个动作执行了,三个术语才会以组合的形式放置到了$terms这个变量中。


在下面这行代码中,放置到变量中的数据就不是数组,而是字符串了:


$text = "北京大学、北京外国语大学、北京语言大学都是开设有翻译专业的高等学府,培养了许多优秀的翻译人才。";


字符串都会用半角双引号("")或者半角单引号('')来包裹住。


字符串和字符串之间是可以拼接的,比如我想把"北京"、"语言"、"大学"三个字符串拼接起来可以这样操作:


<?php
$beijing = "北京";$yuyan = "语言";$daxue = "大学";
$BLCU = $beijing.$yuyan.$daxue;
?>


这里我们用的拼接的操作是通过半角句号(.)来实现的。这个例子告诉我们:编程语言里的标点符号和自然语言中使用的标点符号是很不一样很不一样的!


在有些编程语言里,拼接的操作使用加号(+)来实现的,所以学不同的编程语言时一定要注意区分,不要混淆。


3)循环


不得不说,循环是我编程学习道路上的噩梦,我上高中时就因为看不懂循环而对算法设计、编程这样的知识望而却步。


每个编程语言都有循环,以PHP为例,看这样一个例子:


<?php$terms = array("北京语言大学","北京外国语大学","翻译");
for($i=0;$i<3;$i++){ echo $terms[$i].";";}?>


第2行我们已经分析过了,把三条术语通过array()这个动词放置到$terms这个变量中。


那么放置的次序呢?谁是第一、谁是第二、谁是第三?

 

显然,北京语言大学排第一,北京外国语大学排第二。

 

但是在程序员的世界里,并不是从1开始排的,而是从0开始。

 

所以,我们会说:数组$terms中,“北京语言大学”的索引是0,“北京外国语大学”的索引是1,“翻译”的索引是2。

 

这里的“索引”又是什么呢?

 

不得不说,学编程的过程中我们会接触到许多我们之前也许遇到但从来没有仔细深究过的名词,比如这里的“索引”。

 

其实,我们的姓名、我们的学号、我们的身份证都是我们的索引,“索”是寻找的意思,“引”也是寻找的意思,通过索引别人能够找到我们每一个人。

 

在数据的世界中,索引可以对应特定的数据,比如在$terms这个数组中,0这个索引就对应着“北京语言大学”这条术语。

 

我们如果想从$terms这个数组中提取“北京语言大学”这条数据,就可以使用这种方法:

 

$terms[0]

 

这里的中括号“[]”就像是抓娃娃机的钩子或者挖土机的铲斗:


只要能抓到索引,就能提取到索引对应的数据。

 

所以如果想提取数组中的第一条数据,就使用$terms[0],想提取第二条数据就使用$terms[1],想提取第三条数据就使用$terms[2]。

 

那么如果一个数组中有很多很多条数据怎么办?那我们就得使用循环了,比如:

<?php$terms = array("北京语言大学","北京外国语大学","翻译");
for($i=0;$i<3;$i++){ echo $terms[$i].";";}?>

我们在此处使用了一种叫for的循环,它首先明确了从第几条数据开始提取,其次明确了提取结束的条件;最后明确了每提取一条数据后索引如何变化:

 

$i = 0

$i < 3

$i++

 

$i = 0 的意思就是先定义一个变量$i,将0放置在其中。循环开始后首先先提取第0条数据,即:$terms[0],提取完成后再将其打印出来:


echo $terms[$i];


这里的echo就是打印、显示的意思,而不是我们平时学英语时学到的回声、赞同的意思。编程学习过程中确实有很多这种与实际英文本意不同的英文单词,学习时最好跳出英语词汇的圈圈,重新认识这些英文单词的意思。

 

当我们用上面的代码打印出第0条数据后,再在后面加上一个分号,以方便最后显示打印出来的数据:


echo $terms[$i].";"


注意这里的分号使用双引号包裹起来的,这就表明这是一个字符串,是可以打印出来的,而且这个字符串通过句号与前面的术语连接到了一起。


仔细的朋友又会注意到,怎么最后还有一个分号呢?为什么这个分号没有双引号呢?


echo $terms[$i].";";


这是一个很有趣的问题,我刚开始学习PHP这种编程语言时也不习惯,因为许多其他的编程语言是没有的,但是PHP是有的。这个分号需要加在每一条编程语言后面,以告诉运行程序的系统:当前这条语句我写完了,我用分号结尾,请执行吧。

 

如果一条程序语句写完后没有分号结尾,那么这个程序就会执行错误,因此我们务必要在写PHP程序时养成写分号的习惯。

 

现在大家应该明白这段代码的主体了吧?


<?php$terms = array("北京语言大学","北京外国语大学","翻译");
for($i=0;$i<3;$i++){ echo $terms[$i].";";}?>


现在我们知道了当$i的值是0时,我们可以打印出北京语言大学来。这时候我们会做一个判断:


$i < 3


我们判断的是$i这个变量的值是否小于3,如果小于3,那就执行一个有趣的语句:


$i++


这里有两个加号,合在一起就是:自加1。

 

也就是说:$i原本的值是0,当发现0小于3时,就在0的基础上加一个1,变成:1

 

这就是自加1的意思。

 

当$i的值变成1后,再去判断$i是否小于3,如果小于3就继续执行echo后面的内容,将第二条术语打印出来,然后再自加1。

 

直到$i的值变成3,3是等于3的,而不是小于3的,此时就不会将$terms[3]中的数据提取出来的,最近一次数据的提取就是$terms[2],就这样,数组中三条数据就分别被:$terms[0]、$terms[1]、$terms[2]给提取了出来。

 

这就是循环。实际上它并不是一个环,而是根据给定的条件来逐个执行代码并判断条件,直到这个条件不成立无法继续执行代码。

 

这个例子告诉我们:谁也不是生下来就会编程的,我上高中时曾经因为循环而踌躇不前,没有将算法设计深入学习下去,但这并不代表着一辈子都学不会循环,只要了解这背后的基础知识,学习起来也不难。

 

如果for循环我们掌握了,那么再看看另一种循环:


<?php$terms = array("北京语言大学","北京外国语大学","翻译");
foreach($terms as $term){ echo $term.";";}?>


上面代码的第2行和第6行是我们刚刚讲过的,但第4行的foreach我们没有讲,这也是一个循环,叫foreach循环,它的作用更为直观:

 

foreach就是:对于......中的每一个元素

 

所以:foreach($terms as $term)的意思就是:对于$terms数组中的每一条术语,我们将其用统一的名字命名,即:$term。

 

这个时候我们不用在乎每条术语的索引是什么,只知道foreach循环能够将数组中的每一条术语用这种简单的方法全部循环一遍。

 

这个例子告诉我们:在很多程序语言中,反而是越简单的代码越抽象,越抽象就越难理解,越难理解就越需要在自己的大脑里将抽象的东西具象出来,这个过程既需要教师指导,用有趣的比喻来引导我们思考,也需要我们自己用个性化的方法来辅助理解和记忆。

 

3)函数

 

说起函数,我可能满眼都是泪水,你若问我为什么,我会说是因为我对翻译爱得深沉......

 

函数的英文是function,但中文文字本身并不能反映function的意思。

 

我国清代的数学家李善兰在翻译《代数学》一书时,将“function”翻译成了“函数”。之所以这样翻,他有他的根据。中国古代“函”字与“含”字通用,都有着“包含”的意思,李善兰给出的定义是:“凡式中含天,为天之函数。”中国古代用天、地、人、物4个字来表示4个不同的未知数或变量。这个定义的含义是:“凡是公式中含有变量X,则该式子叫做X的函数。”所以“函数”是指公式里含有变量的意思。

 

大家也许还记得小时候学的数学知识里有这种记号:

 

f(x) = x + 1



这里的f就是function的缩写,我习惯于不将function翻译成“函数”,因为我觉得“函”在今天的汉语里无法对应到编程语言中function的作用,而“数”又让我们觉得自己在处理的是“数字”或“数学公式”,但实际上,function就是一个机器:




你若是在Google里搜索:function machine,会看到很多类似的比喻:



我觉得用一个机器来比喻函数(function)是非常好的方法,因为对于这个机器而言,我们希望向其中输送一些数据,这个机器处理完成后会输出一些数据,这就是函数的作用。


那我们遇到过哪些函数呢?再来看这段代码:



$terms = array("北京语言大学","北京外国语大学","翻译");
$text = "北京大学、北京外国语大学、北京语言大学都是开设有翻译专业的高等学府,培养了许多优秀的翻译人才。";
foreach($terms as $term){ echo $term.":".substr_count($text, $term).";";}


凡是你看到像f(x)这种的都是函数:


array(x)

foreach(x)

substr_count(x)


现在是不是对上面的代码中几个单词的作用就有更新的认识了?


array()函数接收的数据是三个字符串,输出的是一个数组。


foreach()函数接受的是数组$terms,随后在花括号{}中将这个数组中的单词作为原料进一步加工,再用echo来输出加工结果。


substr_count()则是一个即便对我而言都非常陌生的函数,对于这种函数而言,需要去看文档中的功能描述,比如:



一查就发现,这个函数的作用就是计算某个字符串在另一个字符串中出现的次数,所以我们才会这样使用:


substr_count($text, $term)


这个函数的输入是两个变量,这两个变量放在函数名后面的圆括号里,而这个函数的输出则是一个数字,即第二个变量在第一个变量中出现的次数。


通过上面这段分析你会发现,在学习编程语言的过程中,函数就好比我们学习自然语言过程中学到的动词(Verb)一样,我们学到的动词越多,我们就觉得自己对语义的理解更深入。我们经常会去分析句子的主谓宾结构、主系表结构,这里的“谓”和“系”都对应的是动词。


分析主谓宾的过程我们能知道动作的施事者和受事者,在分析代码的过程中我们也要去分析函数的输入和输出,这样才会更加理解一段代码是干什么的。


随着编程语言学习的不断深入,我们会学习到更多的函数,有些函数功能强大,一个函数就能解决我们的问题,有的函数则需要我们自己去创造,将别的函数拿来构建自己的函数,并且去梳理究竟我们要处理什么类型的数据,希望输出怎样的数据。


三、总结


讲到这里,本文最开始那个案例代码中所有知识点都分析完,这是几个非常关键的编程知识点:变量、数组、字符串、循环、函数。


但他们并不是编程知识的全部,仅仅是我给翻译专业学生的第一节编程入门课上想分享的主要内容。


对于翻译专业的同学而言,我希望为大家创造更多的与我们日常学习、工作密切相关的问题场景,并且用编程的手段来解决具体的问题,展示问题解决的过程和逻辑。


如果大家愿意学习更多的译者编程知识,可以学习这本书:



也可以继续关注简言微信公众号之前发布的译者编程知识。


我希望大家能够挖掘更多面向与翻译实践、翻译研究、翻译教学相关的问题场景,基于案例学习编程基础知识,在学习过程中提升自己的思维逻辑。


译者学习编程并非是为了成为一个程序员,而是为了成为更好的译者。译者身边处处有问题,编程可以解决大问题,也可以解决小问题。不妨先从小问题开始磨炼自己的编程技能。


编程的基础知识学习需要耗费不少时间,但不是一件难到无法完成的事情。我们每个人学习编程的过程都会有一个“静默期”或“傻傻期”,要能坚持住,持续学习和回顾,要做好学习笔记,要找到合适的朋友或老师指导你学习,解答你学习的疑惑。


学习编程的过程中我们的思维方式会发生很大的变化,思维逻辑能力的提升是一个渐进的过程,虽然难以量化,但受益终生。