列表&字典解析式

NO IMAGE

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最終的輸出程式碼做了更嚴格的規範,可以用編譯器實際測試一下

字典解析的實現也是類似的,就不贅述了