Lua 小试函数式编程,PBM绘制二维区域

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

一、区域

  举例来说,下面的函数表示一个以点 (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语法

  值得注意的是,上边 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。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值