用Scheme解释器项目来了解Java(一)
本章简介
本章主要介绍简答的scheme语言的语法,还有简单scheme的token所对应的类。
简单介绍scheme语言
有两篇ibm的文章我觉得介绍的很详细,这里我贴出来
scheme语言概要(上)
scheme语言概要(下)
希望大家能在看本篇博客的时候把这两篇介绍scheme语言的语法看一下,因为我自认为写不出这么好的入门博客了。
类的设计
类图
从上面这张图片我们可以看出,所有的scheme语言中的单元,都是继承自SchemeToken类,包括
- Atom:所有关键字,变量名
- String:字符串字面量
- Number:数字字面量
- Quoted:后面单独解释
- Boolean:布尔类型
- List:所有复杂类的父类,eg,函数调用,列表,向量等等。下一章介绍
SchemeToken
类
SchemeToken<T>泛型接口
在实际的解释过程中,因为有不同类型的数据需要处理,比如Number,String,Boolean,而这些数据并没有相同的父类(这里不要谈论Object,没有意义),所以我们就要用SchemeXXX这种类型来对其进行包装,成为SchemeNumber,SchemeString,SchemeBoolean,这些类全都成为SchemeToken的子类。
问题来了,虽然我们成功地通过类的设计使其成为SchemeToken类的子类,但是每个子类里面依然要存放一个基本数据类型,比如
- SchemeNumber:存放Integer类型
- SchemeString:存放String类型
- SchemeBoolean:存放Boolean类型
这里就存在一个共性,即每一个子类都保存一个基本数据类型,如果这个基本数据类型的变量用content来表示,那么在不同的子类中就会用不同类型的content。
- SchemeNumber:content:Integer
- SchemeString:content:String
- SchemeBoolean:content:Boolean
而且为了获取这个content变量,还会有不同返回值类型的getter
,与其这样我们不如将这个特性进行抽象,抽象到SchemeToken类中,于是有了如下代码
package SchemeSimple;
import Impl.Description;
import Impl.Executable;
import Main.Executor;
public abstract class SchemeToken<T> implements Executable {
protected T content;
public T getContent() { return content; }
abstract public Description type();
}
声明这个content为T类型,并且用protected
关键字进行修饰,子类在对SchemeToken进行集成的时候,需要制定这个泛型参数,如下
public class SchemeNumber extends SchemeToken<Integer>
public class SchemeBoolean extends SchemeToken<Boolean>
public class SchemeString extends SchemeToken<String>
这样我们就可以把content的变量声明和对于content的getter
放在父类中进行了,这样,逻辑显得更加清晰。
type()
抽象方法
这个方法存在于SchemeToken.java
中,目的是为了替代instanceof
关键字,因为我们在计算SchemeToken的值的时候,需要频繁的使用instanceof关键字判断类型,这样会很耗时,不如使用一个抽象方法来直接获取类型。简单举例:
(if (< 2 1) 3 4)
这里很明显,(< 2 1)
在我们看来就是一个明显的SchemeBoolean类型,但是在分析的时候,所有的类型都是SchemeToken,如果我们需要判断这个位置的变量是否合法,我们就需要这么做
if (token instanceof SchemeBoolean)
用instanceof进行判断是一个耗时操作,可以用type()
方法进行替代,
if (token.type() == Descriptioin.Boolean)
SchemeQuoted
类介绍
SchemeQuoted
语法举例
-
'1
=>1
-
'a
=>a
-
'"string"
=>"string"
-
'(1 2 3)
=>'(1 2 3)
-
'(define a (lambda (x) (+ x 1)))
=>'(define a (lambda (x) (+ x 1)))
从上面的例子中可以看出,如果对于简单类型前面加上一个'
,那么返回其简单类型。
如果对于一个复杂类型,前面加上一个'
,那么原封不动的返回。
这个符号的出现主要是为了解决一个结构究竟是一个函数调用还是一个数据的问题,比如
(1 2 3)
这种语法在scheme里面,其真正的含义是1(2, 3)
,即1
是一个函数,传入两个实参2
和3
,可是明显,1作为函数名是不合法的,所以,如果程序里面需要(1 2 3)
作为一个数据来用,前面就必须要加上'
用来区分。
'(define a (lambda (x) (+ x 1)))
再看看这个例子,这个例子中,一个函数的定义前面加上了'
符号,即将整个()
中的内容作为一个数据,define
、lambda
、+
都不具有其特殊含义,完全代表一个符号被包装在一个list里面。通俗点说,这里的define
、lambda
和一个普通的abc
没有区别。但是如果程序中有这种写法,目前我没有发现有什么实际意义。
在一个需要'(1 2 3)
的地方:
(apply + '(1 2 3)) => 6
即将+
这个过程对后面的list进行reduce,得到结果6
结语
这一章简单介绍了关于简单类的类图和主要的需要进行重写的方法,下面一章介绍复杂类SchemeList
。
上一篇: Python格式化之%占位符格式化
下一篇: P7324 [WC2021] 表达式求值