python的一个特色就是提供列表/字典/集合等数据结构的解析式,比如:
[i * i for i in xrange(100) if i % 2 == 0]
表示一个包含0到99的所有偶数的平方的列表
larva也实现了这种语法,不过稍微有些区别,其实要实现一模一样的也可以,不过最后没这么做
首先是没做多重嵌套的for和if,python中没有限制列表解析式的for和if的嵌套,larva限制了只能有一个for,或一个for...if,我不想把这个语法搞太复杂,其实大部分时候都是只有一个for的,多重嵌套用的地方很少
其次是for语句的左值,larva只允许变量名,或变量名组成的解包赋值(可以嵌套解包),且这些变量名所在命名空间是独立在这个解析式里面的,比如python中:
a = [i for i in l]
print i
这是可以的,因为i被认为是在a所在的命名空间,会打印它在l最后迭代的值,但是这在larva中是不允许的,列表解析式的左值被认为是临时变量,不会和解析式本身所在的命名空间冲突,其实另一个原因是编译器和代码转换实现的便利性,如果解析式中的左值不单独一个命名空间,则当编译器搜索一个函数的局部变量时,需要扫描所有表达式,比较麻烦
在代码转换实现上,如果独立一个命名空间,解析式就可以简单地转成一个函数。考虑一个解析式:
[A for B in C if D]
这个解析式的实现是:
1 计算C
2 建立一个空列表l
3 对C的结果做迭代,对每个元素,赋值给B,计算D,若D成立,则计算A,结果增加到l中
4 返回l
如果把这个实现写为函数f,则可以将C的值直接传给f,因为只需要它的值,但是A和D需要实现在f内部,而B则可在编译阶段解析好,实现为函数的局部变量
计算A和D的时候,可能只需要B中的变量,也可能要引用解析式本身所在的环境,这些环境数据分情况讨论:
1 全局变量,只需要把f写在当前模块即可
2 若解析式出现在方法中,可能引用当前对象,比如this和super关键字,跟1一样,这时候只需要将f写在类内部即可
3 局部变量,需要传入,因为目标语言可能不支持嵌套函数
一个例子(larva的方法中对于当前对象的属性引用可以省略this,即下面的“.a”相当于“this.a”):
g = 123
m = []
class A:
func __init__():
.a = "hello"
func f():
for i in range(10):
l = [.a + str(g) + j for j, k in m if i]
... //deal with l
这个例子中对l赋值这一项会被转换为类似如下java代码:
l = build_list_compr_0001(m, i);
即局部变量i这时候的值会被传入构造列表解析式的函数,函数的实现类似:
LarObj build_list_compr_0001(LarObj obj, LarObj i)
{
LarObj j, k;
LarObj l = new LarObjList();
for (LarObj iter = obj.iterator(); iter.has_next();)
{
LarObj[] unpack_0001 = unpack_seq(iter.next(), 2);
j = unpack_0001[0];
k = unpack_0001[1]; //解包赋值实现参考前面的日志
if (i.op_bool())
{
l.add(this.a.op_add(str(g)).op_add(j));
}
}
return l;
}
这个函数实现在class A对应的输出代码中,所以能直接访问this和g,如上所述,i是通过参数传入,而j是临时变量,不会影响调用者的命名空间
当然这个例子并非实际的转换结果,比如代码中变量命名等,larva最终的输出代码做了更严格的规范,可以用编译器实际测试一下
字典解析的实现也是类似的,就不赘述了