太玄的Lua编程语言课

0X08变量

前置知识

0X07小学算术

正文

在进行复杂问题的计算时。我们经常需要把解题过程分解成多个步骤进行。把上一步的结果代入下一步中运算。为了保存一些值,我们引入“变量”的概念。变量就像仓库,它可以存储数据。需要存储的时候把数据放入,需要用的时候从中读取数据。

但是变量又和直观的“仓库”有些不同:

往变量中存储数据的过程称为赋值。一次赋值之后,变量中原先存储的数据就会丢弃。

从变量中读取数据不会改变变量的值。也就是一次赋值之后可以读取无数次。因为所谓的“读取",就是从变量中复制数据。

变量由变量名进行区分。我先来介绍如何起一个合格的名字。同时满足后边三个条件的就是合格的变量名。合格变量名示例:var12 myVar Hello _myNumber (这是四个变量名)

变量名只能由大小写字母,数字和下划线组成。不能包括空格或其他符号。

变量名不能以数字开头。

变量名不能与系统中关键字冲突。所谓的关键字是指一些符合变量名前两条规则的词(或者叫“标识符”)。它们在Lua中有特殊的语法作用,因此不应被用作变量名。关键字包括:

and break do else elseif end false for function goto if in local nil not or repeat return then true until while

要给变量赋值,则需要使用等号=(注意,单个等号在Lua并不表示相等关系,而表示把等号右边的值赋予左边的变量)。例如:

经过上一步赋值之后变量myVar的值变成了123,要在计算中使用这个变量,直接把变量名当作数字写在表达式中就可以。

在交互式解释器下,要查看变量的值,只要输入变量名然后回车。

也可以把表达式运算的结果赋予变量。这样就方便另一步运算调用本次运算的结果:

同一个变量可以同时出现在一个赋值符号(单等号)的左右两边。这表示先读取变量的值,进行赋值符号右边的运算,然后把值再赋予变量。例如这个例子中i初始值为6,把i的值加1后再赋予i。执行完第二个赋值语句后i是7。

稍微复杂一点的计算中更能体现变量的作用。参看视频演示:变量使用 https://www.bilibili.com/video/av13928754/

后续推荐

0X09分步算术

0X09分步算术

前置知识

0X08变量

正文

学习过变量之后让我们重新审视一下0X07中所做的练习。一次性把一个复杂的表达式写出来是很费神的,我们可以把复杂的计算过程分为若干步骤。用变量保存每一步的计算结果,然后用于下一步计算,直到得出最后的答案。

题目:124×38+65×124+76÷110÷76×7 解法:先把式子拆成几个小式子,然后再求最终结果。如果拆开的小式子还很复杂就可以进一步拆分。我决定按照这样的顺序计算(124×38)+(65×124)+(((76÷110)÷76)×7)。每一步计算的结果用变量保存,带入下一步计算。代码是:

题目:已知圆的直径为8,求圆的面积。(圆周率取3.14)。 解法:求圆形面积的公式中用到了圆的半径。所以先求出半径然后求面积。圆周率也可以先用变量保存下来。直径用d表示,圆周率用pi表示,面积用S表示,半径用r表示。

答案是50.24。

注意,如果要解方程,不能直接按数学中的写法把方程写出。简单一点的方法是先约简算式,整理为若干步求值赋值,最终得出结果。如果要完全让计算机自动求解方程,需要用到符号运算。但是这就超出了本系列的内容范围。

在3的题目中,一开始需要初始化两个已知的变量,所以分别写了两个赋值语句。如果想精简一点可以用一个多重赋值语句来代替这两个语句:

只用一个赋值符号但是左边有若干个用逗号隔开的变量名,右边有若干个用逗号隔开的表达式(单独一个变量或值也是表达式)。左侧变量名一般与右侧表达式数量相等。对应位置的表达式会给对应位置的变量名赋值。这里右侧第一个表达式8给左侧第一个变量赋值,右侧第二个表达式3.14给左侧第二个表达式pi赋值。

多重赋值也可以用于多于2个变量的赋值。

如果右侧表达式数量多余左侧变量,则多余出的表达式不会给任何变量赋值。

后续推荐

0X0B函数调用

0X0F比较数值

0X0A按位运算

前置知识

0X07小学算数

正文

目前的计算机实现中中存储数据,最终都归结到二进制数。为了保存负数、简化减法运算、处理浮点数等目的。计算机中采用了多种约定。例如反码、补码、IEEE754等。

Lua这种解释性语言一般尽量隐藏底层细节,以便开发人员把精力集中到算法本身。但是Lua还是提供了一些用于底层数据的操作。这就包括这里要介绍的按位运算。

首先按位运算的对象是整数或者小数部分全为零的浮点数——后者可以无损地转换成整数,在进行按位运算的时候,可转换的浮点数会自动转换为整数。

按位运算基于数据实际在计算机中处理的数据形式(二进制补码)运算。对于两个操作数,一位一位地执行与、或、非、异或等操作。或者对于一个操作数,进行一位一位按位取反运算。也可以把一个整数的二进制形式全体位左移若干位、全体位右移若干位。

所用的运算符是

& 按位与

| 按位或

~ 按位异或

右移运算

~ 按位非运算

按位运算主要是针对于目前的计算机实现的。但是这对于并不十分贴近硬件的软件层次和算法来说并不是十分重要。所以如果你没弄明白按位运算并不影响后续课程。本课主要针对在C语言、汇编语言之类的语言中接触过按位运算的读者。

后续推荐

暂无

0X0B函数调用

前置知识

0X09分步算术

正文

虽然针对数字的运算有加减乘除和乘方。但是对于高中生来说,这个计算器还是太简化了,没有办法计算三角函数、对数等。幸好Lua还提供了所谓的标准库。其中的数学模块提供了这类功能。

编程中调用函数的方法和数学上使用函数略有相近。如果你要调用的函数名为f,并且想把变量x给它作为参数。那么调用函数的表达式写出来就和数学上一致:

函数运算后的结果叫做函数的返回值。我们可以把函数调用的表达式直接用在算术表达式中:

如果你直接打开一个Lua虚拟机进程,然后执行上边的命令。肯定会提示错误,没有定义函数f。我们不应该使用未定义的函数,就像我们不该在变量初次被赋值(也叫初始化)之前读取这个变量的值。

下边开始介绍具体能用的函数。函数名字都有点长,由模块名“math”加句点“.”然后再加一个表示函数功能的名称。具体为什么要这样,后边会介绍。这些函数是预定义函数 ,由Lua虚拟机提供,不需要我们定义或导入就可以使用。如果这些函数无法直接使用,尝试先运行一遍导入模块的命令:

求正弦值(弧度制):math.sin 接收一个数字作为参数(默认单位为弧度),返回其正弦值。接收的参数可以是数字也可以是赋予数字值的变量。

视频中会演示一下如何计算一个含有三角函数的式子:调用正弦函数 https://www.bilibili.com/video/av13999251/

求对数函数:math.log 接收一个数字时计算其自然对数值:

它还可以接收第二个参数。用逗号和第一个参数隔开。第二个参数表示选定的底数:

总结一下,调用函数的表达式由三部分组成:函数名,一对圆括号,括号内的参数。关于参数,目前有以下两点注意:

有的函数可以不填参数。但是一对圆括号必须要有。如果不加括号则不表示函数调用,而表示把函数名当变量使用。这个我后续会介绍。

如果要同时传入多个参数,各个参数之间用逗号隔开。而且顺序很重要。

其他的数学函数我会在后续视频中介绍。这不是必看内容。

这里为止,我们通过算符和数学模块函数基本可以计算各种常见算术问题。现在反过来思考一下,难道算符不也是函数么?比如我把(3+2)写作+(3,2)是不是就很像一个函数。事实上算符就是一些特殊函数的调用的简写。以后我们甚至会自定义值类型,然后自定义它们的算符运算法则。这种特性也要运算符重载。

顺便提一下,调用函数时写在括号里的参数叫做实际参数,简称实参。

后续推荐

0X0C函数定义

0X0D数学库函数

0X12字符串

0X0C函数定义

前置知识

0X0B函数调用

正文

在处理求圆的面积之类的问题时容易发现,这类问题的计算步骤基本一样。如果每计算一个圆就把这些命令重新输入一边太过繁琐。

数学上我们可以用一个函数记号来表示非常复杂的计算过程或者根本写不出解析式的对应关系。我们也可以把一系列计算用一个函数来表示。这样每次使用到相同的代码,就写一个函数调用就可以了。

要定义函数首先要给函数起一个名字。在Lua中,函数名也是一个变量名。而变量名的命名规则之前已经介绍过了。

函数定义也是一种“表达式”。我们输入后回车就会执行。

函数定义表达式分为多个部分,我将按部分逐渐构造出它。

函数定义表达式由function和end两个关键词标记首尾。

在function和end之间先写函数名后接一对圆括号。圆括号中需要写形式参数列表,这里我先用不要形式参数的形式。

至此为止,其实已经可以定义函数myFunc。只不过这个函数什么也不做。

数学上的函数有自变量和因变量。在编程中定义函数时,称为形式参数和返回值。现在重定义我的函数myFunc使得,其能计算平方值。

它需要一个形式参数。形式参数也是一个变量,遵循变量命名规则。更多形式参数(简称形参)的问题以后会涉及。这里我把形参命名为x1:

函数名和圆括号里的形参列表叫做函数头。在函数头之后,与function配对的end之前,可以填入“函数体”。函数体就是要执行的计算过程。

这里函数体只有一个语句——称为return语句。由return后接一个表达式。它表示调用函数一次就把这个表达式的值返回调用函数的地方。

执行第6点最后一条定义后。myFunc就可以调用了:

也可以用变量做参数

数学上用具体数值替代未知数,在编程中体现为用值或者变量给定义函数时约定好的形参赋值。执行函数体中的运算,然后返回值。

函数调用过程可以这样近似理解(以myFunc(y)为例):

为了区分定义函数时约定的在函数内使用的变量x1和调用函数时给x1赋值的变量或常数,前者(定义中的x1)称为形式参数(formal parameter);后者(例子中的4或变量y)称为实际参数(actual parameter)。

任何函数在使用前都需要定义。math类函数和print函数,不用定义,就能直接调用。是因为它们是所谓的预定义函数。由Lua虚拟器定义,可以直接使用。

后续推荐

0X11更复杂的函数

0X0D数学库函数

前置知识

0X0B函数调用

正文

首先是除了sin以外的三角函数,它们的值域、定义域和数学上一致。

方便分数值的转化。

可以计算无穷大的反正切值(分母为0)。

math.cos(x) 余弦函数

math.tan(x) 正切函数

math.asin(x) 反正弦函数

math.acos(x) 反余弦函数

以上各式中x是一个参数。涉及到角度的,单位都是弧度。

math.atan(x,y) 反正切函数比较特别。这里的x,y是两个参数。这个函数会返回x/y的反正切值。这样看似在算小数的反正切的时候很麻烦,但是y可以省略。省略y之后默认值为1。例如

反正切函数可以有两个参数的好处有两点:

为了方便角度和弧度转化。提供了转化函数:

math.rad(x) x是角度,返回对应的弧度。

math.deg(x) x是弧度,返回对应的角度值。

math.pi不是函数,而是一个数值常量。使用的时候不需要加圆括号和参数,直接像变量那样用。但是不要赋值(有点像数字常数)。典型用法是配合三角函数:

math.exp(x) 返回自然对数底数的x次幂。

math.fmod(x,y) 取模运算。对于整数来说就是除整取余。其实相当于x%y。这就再次印证了我说的可以把运算符看作是简写的函数。

math.sqrt(x) 求算术平方根。相当于x^(1/2)。

math.abs(x) 求x的绝对值。在学习选择结构和定义函数后可以尝试自己定义一个相同功能的函数。

最大最小值函数。这两个函数接收多余或等于一个的参数。(三个句点表示还能接收任意个参数。)

math.max(x,...) 返回其中最大值。例如:

math.min(x,...) 返回其中最小值。例如:

在学过选择结构、循环结构、比较数值、定义函数后可以尝试定义与之功能相同的函数。

这并不是全部math模块的内容。我把剩下的函数(或math.pi这样的常变量)分为两类:

随机数产生函数。

与计算机中数的编码实现方式有关的放到值的类型章节。如浮点数转换整数、整数变量最大值、无符号整数比较等。

后续推荐

0X0E中学算术

0X0E中学算术

前置知识

0X0D数学库函数

正文

有了三角函数、反三角函数、对数函数、求幂运算,中学阶段涉及的所有具体的数值计算基本就都可以求解。对于分段函数、狄利克雷函数这类需要逻辑判断的函数还需要学习选择结构才能处理。

求此表达式的值:ln(-sin(24))/(e^arcsin(0.9))+(3^(1/2))

第2点中提出的习题,只需要简单转化为Lua中的表达式即可。也可以分为多步计算,用变量保存中间值。这里直接转换:

结果是约为1.69968。

解释一下题中的函数用法。一个函数的返回值可以作为另一个函数的实参。因此可以把一个含有函数调用的表达式写在另一个函数调用的参数位置。

需要注意的是,很多中学的数学题还不能直接通过算术求解。例如求函数的值域、定义域。本质上不是从数到数的计算,而是从函数到数或者从函数到区间的运算。想要靠程序完全自动解决这类问题,还需要用到更多的Lua知识。

后续推荐

暂无

0X0F比较数值

前置知识

0X09分步算术

正文

在数学计算中经常需要比较两个数的大小关系。对于四五位的数来说这不是什么难事,但是如果数据位数很多或者由复杂表达式表达,这个工作就变得比较麻烦。另外使用变量进行算术计算的时候,如果每次遇到需要判断都输出变量值人工判断,也显得很繁琐。

以上的工作都可以交给Lua自动判断。这会引入数字之外的另一种值——布尔值(或者叫逻辑值)。关键字true表示逻辑真,关键字false表示逻辑假。

注意虽然print(true)和print('true')输出效果一样。但是true和'true'是不同类型的。true是逻辑值,而'true'是字符串。

比较运算符:比较运算符和加减乘除一样是运算符(如果你学过函数部分的话,也可以把它们看作一种函数)。它们都接受两个操作数(被比较的数)。然后得出一个比较结果。这个比较结果是逻辑值。

对应于数学中实数大小关系的三歧性——相等,大于,小于。有三种算符。然后再把这三种关系两两组合又有三种关系。一共六种比较算符:

> 大于

== 等于(注意是两个等号连用。并不是赋值,而是相等判断)

>= 大于等于

~= 不等于

以下边表达式为例:

如果比较结果为真,则表示它所代表的关系成立(3大于等于2),否则表示它所代表的关系不成立(3不大于等于2)。根据数学常识这应该返回true。

比较的操作数也可以是变量,进行比较不会改编变量的值:

这个比较就是在判断“234等于150”是否为真。由数学常识可知,两者并不相等。因此比较表达式结果为假false。

视频演示: 比较数值 https://www.bilibili.com/video/av14152406/

从逻辑上说比较运算符就是在判断其对应的大小关系是否成立。成立为真,不成立则假。4

也可以用变量存储比较结果。

既然逻辑值可以用变量存储,那么仅仅是存储就有点无聊了。下一课介绍逻辑值之间的运算。

后续推荐

0X10逻辑运算

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180809G0EDLR00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券