PostScript的领域对象和操作

作为针对桌面出版的文档描述语言,PostScript的设计者力图要解决的核心问题,是如何设计一个灵活高效的语言,以操控桌面出版中各种各样的图形对象,并保证设备无关性。我们不妨戴上语言设计者的眼镜,来模拟一下这个过程。

我们面临的首要问题是如何描述桌面出版中的种种复杂对象和操作。尽管任何平面出版物最终都是二维像素点的集合,但我们并不希望这个语言局限于描述像素点的颜色。这个语言最好还能直接描述文字、线条、形状等设计师熟悉的对象。因为从根本上讲,如果我们要设计的描述语言没有足够的表达能力,不能精简高效地表达图片、字体、形状、颜色等桌面出版领域的业务对象,这个语言将不可避免地“难用”。一般来说,把领域特定语言设计得“好用”,需要深厚的领域知识(domain knowledge)。所幸的是,PostScript的设计者们,原先在施乐PARC从事激光打印机控制语言设计,对于桌面出版可算驾轻就熟。因此,他们毫不费力地选取了Bézier曲线、矢量字体、绘图路径(Path)等作为整个绘图系统的基本结构。在对这些对象的操作上,PostScript选取了平移、旋转、放缩等仿射变换,加上路径操作和字体控制,构成了一个强大但规整的绘图系统。

PostScript绘图系统的设计深刻影响了后来的许多矢量图形系统。举例说,如今计算机使用的矢量字体均采用Bézier曲线描述,即起源于PostScript;如今几乎所有的矢量绘图语言都支持的“路径”,也起源于PostScript。我们不在此详细展开这些领域对象选取背后的原因,对PostScript感兴趣的读者可以阅读《PostScript Language Tutorial & Cookbook》(也称“Bluebook”)了解PostScript的一些基本概念。

PostScript的语言设计

基本领域对象确定后,接下来就是力求设计出一个“灵活高效”和“设备无关”的语言来控制这些领域对象。设计目标落实为具体需求,包含以下三点。

第一,语言本身要能表达曲线、字体、图片、形状等领域对象;颜色、分页及这些对象的平移旋转等操作,在语言里最好也都是一等公民,能直接表达。

第二,语言的表达能力要足够强大,最好是图灵完全,以支持灵活的需求。

第三,语言要与设备无关,也就是说,语言将运行在一个虚拟机或解释器上,而非直接编译为二进制代码。

考虑到我们要设计的语言是针对桌面出版的,最终还要加上一条:这个语言的语法和结构要足够简单,使得非编程专业人士也能使用。

有了需求的指导,我们不难理解PostScript所采取的设计:以一个易用的、图灵完全的语言作为蓝本,加入众多针对桌面出版的对象操作,并实现一个轻量的、与设备无关的解释器。事实上,PostScript是以FORTH语言作为蓝本设计的。选取FORTH的主要原因,是因为它是一个轻量级的、基于栈虚拟机的语言。FORTH的表达能力和易用性当时已被实践所证明,因此借用它的基本控制语法就是一个很自然的选择。

逆波兰表示法和度量单位

逆波兰表示法是FORTH和PostScript等基于栈的语言的一个鲜明特点。在ALGOL家族语言中,3乘以4的一般写法是3 * 4,即运算符中缀。PostScript将运算符后缀,写作“3 4 mul”。意思是将3、4分别推入栈中,然后将乘法(multiply)操作运用于两个栈顶元素(弹出),并将乘积结果入栈。FORTH仍然采用+、*等数学符号。PostScript规范化了所有的操作符,一致采用add、mul等单词操作符来代替+、* 等传统的中缀操作符。我们稍后将阐明规整化的优点。这里只需要了解一点:PostScript程序本质上是一个后缀表达式。PostScript没有所谓的语法,只有栈操作。如果非要说有语法,那就是逆波兰表示法。这一点非常类似于LISP——所谓的语法,就是S表达式。

PostScript允许以闭包定义新操作符,其中,闭包是放在{}中的后缀表达式。例如,“乘以3”这个操作可定义为:/mul3 { 3 mul } def。这里,/mul3表示取“mul3”的符号值。{ 3 mul }是一个闭包,而def将mul3这个符号,映射到{ 3 mul }闭包。据此,4 mul3即为4 3 mul。

其实,从语法上看,/mul3 { 3 mul } def和3 4 mul并没有明显的不同:都是前两个操作元入栈,最后一个操作符进行运算。也就是说,PostScript的栈是异构的,符号、数字和闭包都可以放入栈中。许多操作符如if,也依赖于栈上有一个布尔值和一个闭包。这种不在栈中区分代码和数据的设计,允许我们重写栈上的闭包。实际上我们可以证明这个特性等价于LISP里的宏(Macro)的表达能力,限于篇幅,我们不在这里展开。

现在,我们从mul3这个平淡无奇的例子出发,定义一个英寸(inch)的操作符:/inch {72 mul} def。一眼看去,{72 mul}是闭包,而inch是长度单位,两者毫不相干,为何强拉在一起?原来,PostScript的基本长度单位是1/72英寸,因此5 inch即展开为5 72 mul,或者说360个基本单位。Inch的定义使得我们可以书写1.2 inch 2.3 inch moveto这样直观的程序。

用闭包定义常用度量单位在PostScript中并不少见。对于从未接触过这种定义方法的读者来说,相信inch这个例子让人印象深刻,因为它昭示了度量单位的实质:度量单位是后缀闭包。比如我们说10美元时,已在自觉或不自觉地将“美元”单位替换成 {汇率 mul}闭包,换算成60元人民币等。实际上,任何度量单位之所以能被我们感知,都是因为我们脑中的一个潜在后缀闭包的作用。在摄氏度体系下的人对华式温度没有感觉,或者仅接触一定数量级范围内的人对大数字不敏感,都是由于一个原因:我们尚未建立一个将不熟悉的单位或数量级转化为可感知的单位或数量级的闭包。

PostScript的运行时字典栈

除基本控制语法外,PostScript引入了对于图形处理很重要的两个基本数据结构:字典和数组。可以想象,存有一系列点的数组可以表达一个字符的轮廓,而字典可以很好地表达一套字体。不仅如此,通过字典栈这个概念,PostScript具有了FORTH和其他栈语言所完全不具有的动态特性。我们仍然以一个例子说明。

我们定义一个求直角三角形斜边长度的操作hyp,即/hyp { dup mul exch dup mul add sqrt } def(这里dup表示重复栈顶元素,exch表示交换栈顶两元素,sqrt为平方根,读者可以自行验证这个函数的正确性)。 这里,3 4 hyp得到5。

对解释器来说,我们新定义的hyp与mul并没有本质的不同(后缀表达式和规则化带来的便利)。解释器处理这些操作符时,无论是语言预先定义还是用户定义的,不可避免地需要进行符号表查找。可能的区别仅是到不同的符号表里查找。进一步说,一个叫inch的符号在没有进行符号表查找之前,我们根本不能确定这究竟是一个变量,还是一个闭包。

为了一致地处理符号表的查找操作,PostScript引入了字典栈(dictionary stack)的概念。字典栈是一个由解释器维护的栈,而栈中的元素则是作为符号表的字典。解释器启动后,系统字典systemdict中含有所有预定义操作符和变量,如add、mul等。用户字典userdict将涵盖自定义的操作符和变量。用户也可以随时建立新的字典插入字典栈中。

以字典方式存储符号表是容易理解的,可是为什么需要把这些字典加入“栈”中呢?原来,PostScript是按栈的顺序在字典中寻找操作符的。假如定义“/mul {add round} def”,则当前字典中的mul会被优先使用,而系统定义的mul不再可见。乍看之下,这和面向对象语言里提到的运算符重载概念类似。实质上,PostScript的设计要灵活许多。

首先,因为字典栈的存在,每个运算符都自动有了作用域(预定义的运算符因为存在于systemdict中,从而有全局作用域)。通过字典栈,我们可以实现其他语言中的lambda表达式或者Java中的匿名内部类。PostScript的运算符本质上是动态作用域的,但因为字典栈的存在,我们可以轻松实现词法作用域,方法即是在作用域中临时定义一个字典,在字典中定义新的操作符,并将字典推入字典栈。这样,只要在作用域结束时弹出临时字典,操作符定义也随之撤销。许多PostScript程序都采用这种方法构建。

其次,字典栈巧妙地支持了局部变量。和闭包一样,局部变量的本质是有作用域的值。基于栈的语言对函数局部变量是不友好的,因为局部变量本身是对处理器寄存器的抽象,访问局部变量也是采取随机存取而非按栈顺序存取的方式。而栈机器本身不直接支持寄存器抽象。熟悉JVM的读者都知道,JVM的{a,i,l,f,d}{load,store}系列指令,非常繁冗地支持局部变量数组和栈之间的转存。在字典栈中,局部变量有了优雅的解决方法:通过建立临时字典,我们可在不引入复杂的转存操作下,随机存取随机变量,而且局部变量的作用域得到了保障。比如,以下程序定义了一个叫做local_variable的局部变量,作用域仅限于/sample_proc。而将something换成{something}闭包,即是一个局部的操作符定义。

PostScript语言里的珠玑相关推荐

  1. 有关Adobe公司的PostScript语言授权问题

    各位大神好:     最近公司打算用PostScript语言开发一款打印机产品,本人现在正对这方面予以了解,但是关于Postscript却一无所知,请问园子里的大神们有没有知道的.     我是想咨询 ...

  2. ebnf描述c语言语句结构,EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则...

    Presentation on theme: "EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构:并用操作语义的方法描述对应的语义规则"- Pre ...

  3. c语言里变量列表,嵌入式C语言里的土豪们之变量类型

    嵌入式C语言里的土豪们之变量类型本文引用地址:http://www.eepw.com.cn/article/184332.htm 上一篇我们谈到了运算奢华大户除法(详见<嵌入式C语言里的土豪们之 ...

  4. c语言sqlist结构体,c语言里 sqlist

    满意答案 cielkong 2018.08.12 采纳率:43%    等级:9 已帮助:463人 c语言里 sqlist?//定义顺序表L的结构体 typedef struct { Elemtype ...

  5. C/C++语言里的near和far是什么意思?

    2019独角兽企业重金招聘Python工程师标准>>> C语言里的near和far是什么意思?-CSDN论坛-CSDN.NET-中国最大的IT技术社区 http://bbs.csdn ...

  6. C语言中负数补码的方法,c语言里求负数补码的总结不足与优点.docx

    c语言里求负数补码的总结不足与优点 看C语言编码转换--------负数的二进制表示方法 XX-09-0710:49:17|分类:|标签:|举报|字号订阅 今天在看C语言编码转换时,既然对负数的二进制 ...

  7. Go 语言里怎么正确实现枚举?答案藏着官方的源码里

    在编程领域里,枚举是用来表示只包含有限数量的固定值的类型,在开发中一般用于标识错误码或者状态机.拿一个实体对象的状态机来说,它通常与这个对象在数据库里对应记录的标识状态的字段值相对应. 在刚开始学编程 ...

  8. 聊聊在Go语言里使用继承的翻车经历

    Go不是面向对象的语言,但是使用组合.嵌套和接口可以支持代码的复用和多态.关于结构体嵌套:外层结构体类型通过匿名嵌套一个已命名的结构体类型后就可以获得匿名成员类型的所有导出成员,而且也获得了该类型导出 ...

  9. 把别人的Tcl/Tk代码加入到Go语言里2 矩形

    为什么80%的码农都做不了架构师?>>>    a 从互联网得到的一段tcl/tk代码,把她加入到go语言里 package main import "github.com ...

  10. c语言2 amp 3结果,C语言里23=什么?

    C语言里2&3=什么?以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! C语言里2&3=什么? 这个是按位 ...

最新文章

  1. python自动化办公兼职-python如何实现自动化办公?
  2. c++ try_catch throw
  3. python读取dicom格式的图像并转为png格式
  4. Django的CBV与FBV
  5. 分治策略之最大子数组问题
  6. 浙大翁恺pat练习题_单词长度(翁恺老师C语言入门第八周测试题1)
  7. Mysql数据库索引原理及算法原理
  8. 使用maven创建web项目
  9. Web自动化测试中的接口测试
  10. 直方图均衡化计算过程步骤
  11. PostgreSQL在何处处理 sql查询之五十四
  12. linux里面有mysql的僵尸进程_linux 如何清理僵尸进程
  13. 磨刀不误砍柴工,使用visual studio之前应该先了解这些...
  14. 字典 python 引用_Python字典引用的应用
  15. linux如何共享网络打印机,Ubunt如何安装网络打印机的详细图文步骤
  16. 微型计算机相关的英文文献,微型计算机控制系统(单片机控制系统)外文文献翻译.doc...
  17. LabVIEW视觉功能模块下载
  18. 微信摇一摇周边新功能上线
  19. r1笔记第9天 逻辑英语随堂笔记 (01)
  20. 架构模式之 CS和BS的区别

热门文章

  1. java id 锁_java 多线程synchronized同步锁锁住相同用户Id
  2. 第十五周助教工作总结——NWNU李泓毅
  3. 禅道二次开发(三):二次开发实例
  4. 请别相信她成本多少?个人参与有什么优势?安全可靠吗?
  5. 400GE燎原前夜,智能IP网络的核心路由器巅峰际会
  6. MonthCalendar 的使用
  7. 手机短信(SMS)工作原理(一)
  8. (GIS可视化)热力图
  9. 美国网络再次“瘫痪”,华为意外“出头”,网络服务器世界第一
  10. 蜀门 - 青城加点完美攻略