借鉴《Lua程序设计(第四版)》第9章 – 闭包,9.4节 – 小试函数式编程。
  目标是开发一个用来表示几何区域的系统,其中区域即为点的集合。
  为了实现这样的一个系统,首先需要找到表示这些图形的合理数据结构。我们可以尝试使用面向对象的方案,利用继承来抽象某些图形;或者,也可以直接利用特征函数(characteristic or indicator function)来进行更高层次的抽象(集合 lua函数中function参数 lua 函数式编程_Lua 的特征函数 lua函数中function参数 lua 函数式编程_Lua_02 是指当且仅当 lua函数中function参数 lua 函数式编程_lua函数中function参数_03 属于 lua函数中function参数 lua 函数式编程_Lualua函数中function参数 lua 函数式编程_字符串_05 成立)。鉴于一个几何区域就是点的集合,因此可以通过特征函数来表示一个区域,即可以提供一个点(作为参数)并根据点是否属于指定区域而返回真或假的函数来表示一个区域

一、区域

  举例来说,下面的函数表示一个以点 (1.0, 3.0) 为圆心,半径为 4.5 的圆形区域:

function disk1(x, y)
	return (x - 1.0)^2 + (y - 3.0)^2 <= 4.5^2
end

高阶函数词法定界,可以很容易地定义一个根据指定的圆心和半径创建圆盘的工厂:

function disk(cx, cy, r)
	return function(x, y)
		return (x - cx)^2 + (y - cy)^2 <= r^2
	end
end

  形如 disk(1.0, 3.0, 4.5) 的调用会创建一个与 disk1 等价的圆盘,如下所示:

print(disk(1, 1, 2))			--> function: 0x5581a2fb9bb0
print(disk(1, 1, 2)(1.5, 1.5))	--> true
print(disk(1, 1, 2)(2.5, 2.5))	--> false

  下面的函数创建了一个指定边界的轴对称矩形:

function rect(left, right, bottom, up)
	return function(x, y)
		return left <= x and x <= right and bottom <= y and y <= up
	end
end

  按照类似的方式,可以定义函数以创建注入三角形或非轴对称矩形等其他基本图形。每一种图形都具有完全独立的实现,所需的仅仅是一个正确的特征函数。

二、区域的交、并、补、差

  接下来考虑如何改变和组合区域。我们可以很容易地创建任何区域的补集(complement):

function complement(r)	-- region
	return function(x, y)
		return not r(x, y)
	end
end

print(complement(disk(1, 1, 2))(1.5, 1.5))	--> false
print(complement(disk(1, 1, 2))(2.5, 2.5))	--> true

  同理,交、并、差也很简单:

function union(r1, r2)  -- 并
	return function(x, y)
		return r1(x, y) or r2(x, y)
	end
end

function intersection(r1, r2)  -- 交
	return function(x, y)
		return r1(x, y) and r2(x, y)
	end
end

function diff(r1, r2)  -- 差
	return function(x, y)
		return r1(x, y) and not r2(x, y)
	end
end

三、区域移动

  以下函数可以按照指定的增量平移指定的区域:

function translate(r, dx, dy)
	return function(x, y)
		return r(x - dx, y - dy)
	end
end

四、区域绘制

  为了使一个区域可视化,我们可以遍历每个像素进行视口(viewport)测试;位于区域内的像素被绘制为黑色,位于区域外的像素被绘制为白色。为了简便,使用 PBM(可移植位图,portable bitmap)格式来绘制指定区域。
  PBM 文件结构简单且高效。PBM 文件的文本形式以字符串 “P1” 开头,接下来的一行是图片的宽和高(以像素为单位),然后是对应每一个像素、由 1 和 0 组成的数字序列(黑为 1,白为 0,数字和数字之间由可选的空格分开,最后是 EOF。下面的函数 plot 创建了指定区域的 PBM 文件,并将虚拟绘图区域 (-1, 1], [-1, 1) 映射到视口区域 [1, M], [1, N]中。并绘制了一个南半球所能看到的峨眉月:

function plot(r, M, N)
	io.write("P1\n", M, " ", N, "\n") -- 文件头
	for i = 1, N do
		local y = (N - i * 2) / N
		for j = 1, M do
			local x = (j * 2 - M) / M
			io.write(r(x, y) and "1" or "0")
		end
		io.write("\n")
	end
end

c1 = disk(0, 0, 1)
plot(diff(c1, translate(c1, 0.3, 0)), 500, 500)

  程序中输出到了控制台,复制到文件,并改后缀名为 .pbm 后,打开可以看到下图:




lua函数中function参数 lua 函数式编程_Lua_06


五、Lua语法

  值得注意的是,上边 plot 函数中有一个 io.write(r(x, y) and "1" or "0"),这句和 C++ 的 ?: 表达式一样的效果,第一项为真,就是 “1”,否则就是 “0”。
  例如:max = (x > y) and x or y   证明如下:

  • 若 x > y,则 (x > y) 为 true,则 (x > y) and x 为 x,则 max = x or y = x;
  • 若 x <= y,则 (x > y) 为 false,则 (x > y) and x 为 false,则 max = false or y = y。