用Scheme解释器项目来了解Java(二)

8 篇文章 0 订阅

本章简介

本章主要介绍SchemeToken类的子类SchemeList,这个类是所有复杂类的父类,所有的SchemeList的子类包括自身都具有用()包含的结构,比如

(+ 1 2 3)
(map + '(1 2 3) '(2 3 4))

类的设计

类图

这里写图片描述看起来很复杂,下面我会一一介绍每个类的作用

子类介绍

这里为了展现子类中的每个元素所对应的成员变量,我就用数学公式表示,不用代码表示了,还未实现的先不进行列举,但是如果把下面的结构联系代码看懂了的话,不难推出其他的结构。
- SchemeArithmetic

(+OpName:String1234content:List<SchemeToken>)

- SchemeNumberCompare
(>OpName:String1Arg0:SchemeToken2Arg1:SchemeToken)

- SchemeStringCompare
(>OpName:String"abc"Arg0:SchemeToken"abcd"Arg1:SchemeToken)

- SchemeDefine or SchemeSet
(define:SchemeAtomaName:String1Value:SchemeToken)

(define:SchemeAtomaName:String(lambda(x)(+x1))Value:SchemeToken)

- SchemeCall
(funProcedure:SchemeToken12Args:List<SchemeToken>)

- SchemeApply
(apply:SchemeAtom+MapFunc:SchemeToken(123)Arg:SchemeToken)

- SchemeProcedure
(lambda:SchemeAtom(xy)Paras:SchemeList(xy)Body:List<SchemeToken>)

- SchemeIf
(if:SchemeAtom(<21)Code:SchemeToken3TrueBody:SchemeToken4FalseBody:SchemeToken)

BuiltInOperation<T, U, R>接口介绍

接口代码

import java.util.function.BiFunction;

/**
 *
 * @param <T> the type of arg1
 * @param <U> the type of arg2
 * @param <R> the type of result
 */
public interface BuiltInOperation<T, U, R> {

    BiFunction<T, U, R> Operation();

}

实现了此接口的类

观察上面的类图,可以发现,有三个类实现了BuiltInOperation接口,分别是
-SchemeArithmetic+, -, *, /,%
-SchemeNumberCompare>, <, <=, >=, =
-SchemeStringComparestring?, string>?, string>=?, string<=?
冒号前面是类名,后面是内置(BuiltInOperation)的操作,其实还有更多的内置的操作,这里就不一一列举了,如果全部列举,对于学习Java语言本身和解释器本身都没有好处。

这么设计的原因

其实说一个题外话,我的代码的设计风格都是模仿日本的一位程序员-青木峰郎的设计风格,感觉这位程序员在类的设计方面,即使有很少的共同之处都会对其进行抽象。我这里也是,即使只有三个类需要实现这个接口,我还是对其进行了抽象,之后只要有这个类的instance调用了Operation()方法就可以获取对应的BuiltInOperation

代码举例

这里以SchemeArithmetic.java举例,配合完整的加法操作

public class SchemeArithmetic extends SchemeList
        implements BuiltInOperation<SchemeNumber, SchemeNumber, SchemeNumber>{

    private String OpName;

    ......

    @Override
    public BinaryOperator<SchemeNumber> Operation() {
        switch(OpName) {
            default:
                System.out.println("illegal operation name");
                System.exit(0);
            case "+": return (SchemeNumber n1, SchemeNumber n2) ->
            {return new SchemeNumber(n1.getContent() + n2.getContent());};
            case "-": return (SchemeNumber n1, SchemeNumber n2) ->
            {return new SchemeNumber(n1.getContent() - n2.getContent());};
            case "*": return (SchemeNumber n1, SchemeNumber n2) ->
            {return new SchemeNumber(n1.getContent() * n2.getContent());};
            case "/":return (SchemeNumber n1, SchemeNumber n2) ->
            {return new SchemeNumber(n1.getContent() / n2.getContent());};
            case "%":return (SchemeNumber n1, SchemeNumber n2) ->
            {return new SchemeNumber(n1.getContent() % n2.getContent());};
        }
    }
}

根据接口中的定义,如果调用Operation()返回的是一个函数式接口BiFunction,假设我们这个类中的OpName是一个 +,那么返回的函数式接口就是

(SchemeNumber n1, SchemeNumber n2) ->
            {return new SchemeNumber(n1.getContent() + n2.getContent());}

这是一个lambda表达式,接受两个SchemeNumber类型的操作数,返回一个新的SchemeNumber对象。
此时如果我们要进行如下的加法操作要怎么办呢

(+ 1 2 3 4 5)

我们都知道,加法操作只能接受两个操作数,可是这个表达式后面有不定个数的操作数,从这里我们可以进行推断,这里其实是一个reduce操作的语法糖,实际的语法在Scheme里面也可以表示:

(apply + '(1 2 3 4 5))

那在解释的过程中我们该如何进行表示才能最方便呢,这就需要用到Java8函数式编程风格了

//suppose token:SchemeArithmetic
List<SchemeNumber> Args = token.getArgs();
BiFunction<SchemeNumber, SchmemNumber, SchemeNumber> func = token.Operation();
SchemeNumber Result = Args.stream().reduce(func).get();

这里将所有的参数作为一个流进行看待,然后用func对其进行reduce即可.

SchemeList的意义

SchemeList是一个具体类而不是一个抽象类的原因

其实在这里我自认为SchemeList的地位是有点尴尬的,因为如果要表示一个列表数据,我们可以用'(1 2 3)来进行表示,如果要表示一个调用,我们可以用(f 1 2)来进行表示,但是,有没有一种情况是完全用(x y z)来表示的呢,肯定是有的,所以这里我没有把这个类设计为一个抽象类,而是可以真真正正的表示一个数据结构

SchemeList的出现场景

(lambda(xy)SchemeList(+xy))

(cond((=12)3)SchemeList(else4)SchemeList)

这几种场景下,或者说类似的场景下,我们可以用SchemeList进行数据的包装,但是这个类不能用于计算,如果说出现了对于SchemeList类的计算,那么就会被视为非法。在我的代码中,有以下代码进行代码提示

public SchemeToken execute(SchemeList Token) {
        Error.Print("cannot evaluate the raw list");
        return null;
    }

结语

本章介绍了SchemeList类及其子类的概况,下一张介绍语法分析中如何根据token流分析出一个SchemeList,或者是其子类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值