2017-08-29 46 views
1

我已經建立了自己的Yaml Parser,使用haxe中的更多高級內容(例如摘要)來更好地理解每件事物是如何工作的,並且我又打了另一面牆。通過未知或動態類型的字符串ArrayAccess

重要的部分是摘要,abstract YamlMap (StringMap<Either<String, YamlMap>>),它充當我的數據的容器。一個「節點」可以是一個字符串或另一個yamlmap,在樹的更深處。

這個YamlMap有多種獲取數據的方法,如getMap(key):YamlMapgetString(key):String,以及一個動態訪問一個,getDynamic(key):Dynamic

不幸的是,似乎只有一個@:arrayAccess將工作是抽象的或我失去了一些東西。它似乎也不能用字符串「動態訪問」一個動態對象,或者至少編譯器阻止我這樣做。

所以,這個工程:data.getMap('test_node').getMap('sub_node1').getString('value2') 但這並不:data['test_node']['sub_node2']['value2']

如果我設置getDynamic作爲了ArrayAccess,它告訴我sub_node2應該是一個int。但是,如果我將getMapgetString都設置爲arrayAccess,它總是會調用第一個arrayAccess標記的方法。所以它要麼嘗試獲得「價值」(這是一個字符串,但代碼試圖獲得地圖)或不編譯,因爲,我猜,它試圖從字符串訪問字符而不是地圖位置。

所以,我的猜測是,與這個manual entry有關,arrayAccess對任何不抽象的東西都被鎖定爲一個int,因此動態對象拒絕用一個字符串來訪問。

我能想到的一種可能的解決方案是,不是使用動態值,而是返回某種在投射時會「變平」爲正確類型的抽象。會有其他方法在動態地圖上實現字符串化數組訪問嗎?

注:在某種程度上,這是出於好奇,因爲當前方法使用地圖和字符串的不同調用對日常使用來說足夠好。我也知道現有的yaml haxelib,但這是一種學習體驗,因爲它試圖取代有時可能會出現問題的haxelib。

這是YamlMap抽象的Pastebin爲任何感興趣的人。

+1

關於你的'@:arrayAccess'問題,它看起來像每個參數類型只能有一個(忽略返回類型)。所有的訪問器都在'String'參數上運行,所以它們會發生衝突。一般來說,是的,製作「動態字符串訪問對象」是非常棘手的。你的返回類型必須是能夠鏈接訪問者的抽象本身(例如'obj ['a'] ['b']'),但是你怎麼知道你什麼時候在葉子上?我試着編寫一個類似JS的對象。根據我的經驗,它從來不是我想要的。 –

回答

2

首先,抽象可以有多個@:arrayAccess方法:

abstract MultiArrayAccess({}) from {} { 
    public function new() this = {}; 
    @:arrayAccess function getInt(i:Int) return i; 
    @:arrayAccess function getString(s:String) return Std.parseInt(s); 
} 
var i = new MultiArrayAccess(); 
trace(i[1]); // 1 
trace(i["3"]); // 3 

這一個你的例子之間的不同之處在於主要類型是不同的。這是必要的,因爲在摘要(和一般摘要)上的數組訪問是編譯時間功能。下面是這兩個陣列的AST轉儲訪問的樣子:

_Main.MultiArrayAccess_Impl_.getInt(i, 1) 
_Main.MultiArrayAccess_Impl_.getString(i, "3") 

因此,編譯器知道在編譯時調用哪個方法。在你的情況下這是不可能的,因爲這兩種方法的密鑰類型都是String。通過將getString()getMap()替換爲[],您正在丟失信息。


這似乎也不能「了ArrayAccess」動態對象用繩子,或至少編譯器阻止我這樣做。

這是正確的,在Dynamic上不允許訪問數組。但是,標準庫有一個摘要,稱爲haxe.DynamicAccess,它是一個摘要,使用Reflect.field()實現數組訪問。當然,這會導致運行時開銷。您可以有基於這樣這是完全動態的實現:

typedef YamlMap = haxe.DynamicAccess<YamlMapValue>; 

abstract YamlMapValue(YamlMap) from YamlMap { 
    @:arrayAccess function get(key:String):YamlMapValue { 
     return this.get(key); 
    } 

    @:arrayAccess function setMap(key:String, value:YamlMap) { 
     Reflect.setField(this, key, value); 
    } 

    @:arrayAccess function setString(key:String, value:String) { 
     Reflect.setField(this, key, value); 
    } 
} 
var data = new YamlMap(); 
data["test_node"] = new YamlMap(); 
data["test_node"]["sub_node1"] = new YamlMap(); 
data["test_node"]["sub_node1"]["value2"] = "foo"; 

var map:YamlMapValue = data["test_node"]["sub_node1"]; 
trace(map["value2"]); // foo 

但是,這不一定是一個好主意,因爲你失去了很多類型安全的(和結果,性能)這種方式。

+1

謝謝你的光榮榜樣,Jens。然而,我所看到的一個問題是,從字符串中獲取字符串(在葉節點處)很困難。例如'var s:String = data [「test_node」] [「sub_node1」] [「value2」];'給出:'YamlMapValue應該是String'。在這裏玩它:https://try.haxe.org/#B83C7 –

+1

好吧,@ oli_chose123這裏是一個'toString',可以讓你更接近你想要的:https://try.haxe.org/#5F6c3 - - 或者甚至只是'@:toString():String return untyped this;' –

+0

再次感謝您的一個很好的答案,並感謝@JeffWard這個補充。所以它就是這樣,它是每個訪問者的一個arrayAccess,而不是每個返回類型。我的錯。然後,我認爲不是使用太多的反射,而是在地圖上使用數組訪問('''map ['node1'] ['node2']。getString('potato')''')或者實驗在某種抽象的字符串訪問器上。從技術上講,這會在編譯時使它成爲兩個不同的訪問器,不是嗎? (字符串)'''''''''''''''''''''''''''' –

相關問題