2012-07-15 56 views
1
創建動態閉包

但是也有一些相似,但明顯不同,我需要提出這是一個新的問題,在這裏的一些其他問題:Groovy的DSL:從字符串

我已經創建了一個空類,讓調用它測試。它沒有任何屬性或方法。然後,我通過圖一鍵/值對的迭代,動態創建命名爲關鍵屬性和包含的價值......像這樣:

def langMap = [:] 
langMap.put("Zero",0) 
langMap.put("One",1) 
langMap.put("Two",2) 
langMap.put("Three",3) 
langMap.put("Four",4) 
langMap.put("Five",5) 
langMap.put("Six",6) 
langMap.put("Seven",7) 
langMap.put("Eight",8) 
langMap.put("Nine",9) 

langMap.each { key,val -> 
    Test.metaClass."${key}" = val 
} 

,現在我可以這樣創造了一個新的方法來訪問這些:

Test.metaClass.twoPlusThree = { return Two + Three } 
println test.twoPlusThree() 

雖然我想做的是動態加載一組來自String的指令,比如「Two + Three」,創建一個方法來實時評估結果,然後迭代地重複這個過程然而很多包含我碰巧擁有的表達式的字符串。

問題: a)首先,是否有一個更好更優雅的方式來做到這一點(根據我給出的信息)? b)假設此路徑可行,從字符串動態構造閉包的語法是什麼,其中字符串引用的變量名稱僅在該類的方法中有效?

謝謝!

回答

2

我認爲正確的答案取決於你實際要做的事情。輸入字符串可以是一個更復雜的表達式,如'(Two + Six)/Four'

如果您想允許更復雜的表達式,您可能希望直接評估字符串爲Groovy表達式。在groovyConsole中或Groovy腳本里面,你可以直接調用evaluate,這將在腳本的上下文中計算表達式:如果你是在那些腳本友好的世界的一個

def numNames = 'Zero One Two Three Four Five Six Seven Eight Nine'.split() 

// Add each numer name as a property to the script. 
numNames.eachWithIndex { name, i -> 
    this[name] = i 
} 

println evaluate('(Two + Six)/Four') // -> 2 

,您可以使用GroovyShell類:

def numNames = 'Zero One Two Three Four Five Six Seven Eight Nine'.split() 
def langMap = [:] 
numNames.eachWithIndex { name, i -> langMap[name] = i } 

def shell = new GroovyShell(langMap as Binding) 
println shell.evaluate('(Two + Six)/Four') // -> 2 

但是,要知道,using eval is very risky。如果輸入字符串是用戶生成的,我不會推薦你這樣去;用戶可以輸入諸如"rm -rf /".execute()之類的內容,並根據腳本的特權,擦除腳本執行的任何地方的所有內容。你可以首先驗證輸入字符串是否「安全」(也許檢查它只包含已知的運算符,空格,括號和數字名稱),但我不知道這是否足夠安全。

另一種方法是爲這些表達式定義自己的迷你語言,然後使用類似ANTLR的東西來解析它們。但是,這又取決於你想要完成什麼。

+1

效果很好!我肯定需要使用GroovyShell版本,因爲我會加載「語言」,然後調用未知數量的表達式。感謝關於用戶在表達式中創建的值的提醒......我需要確保首先檢查表達式,以確保它在執行之前僅使用批准的運算符和變量名稱。謝謝一堆! – 2012-07-15 02:33:46