[英]Changing recursive Closure to TrampolineClosure in Groovy
我需要構造從一個字符串僅包含字符的所有可能的二進制表示0
, 1
,和V
(通配符)。 字符串可以是任意長度(超過1000個字符),但通配符的數量小於20。
例如,對於輸入V1V
,輸出將是[010, 011, 110, 111]
V1V
[010, 011, 110, 111]
我當前的實現工作正常,但是使用適量的通配符溢出堆棧。 代碼在這里運行 ,如下所示。
def permutts
permutts =
{
if (!it.contains('V'))
return [it]
def target = it
def res = []
['0', '1'].each
{
def s = target.replaceFirst(~/V/, it)
if (s.contains('V'))
{
res += permutts(s)
}
else
{
res << s
}
}
res
}
println permutts('V1V')
我試圖遵循使用trampoline()
一些例子,但我甚至不確定這是否是正確的方法。 API說 ,“......該函數應該執行計算的一個步驟......”但每個步驟執行兩個動作:在0
和1
代入V
這是我的一次嘗試,可以在這里運行 。
def permutts
permutts =
{ it, res = [] ->
println "entering with " + it + ", res=" + res
if (it.contains('V'))
{
def s = it.replaceFirst(~/V/, '1')
permutts.trampoline(s, res)
s = it.replaceFirst(~/V/, '0')
permutts.trampoline(s, res)
}
else
{
res << it
}
}.trampoline()
println permutts('VV')
輸出是:
entering with VV, res=[]
entering with 0V, res=[]
entering with 00, res=[]
[00]
至少它在做什么,但我不明白為什么它不繼續。 任何人都可以解釋我做錯了什么或提出了解決這個問題的不同方法嗎?
Groovy的trampoline()
提供尾調用優化 ,因此它應該用於在最后執行的指令(尾部)中調用自己的閉包/方法。
因此,更好的解決方案是經典的“頭/尾”處理(添加println來跟蹤調用):
def permutts
permutts = { s, res ->
if (s.length() == 0) {
println "s = $s, res = $res"
res
} else {
println "s = $s, res = $res"
if (s[0] == 'V') { // s[0] ~ list.head()
res = res.collect({ it = it + '0' }) + res.collect({ it = it + '1' })
} else {
res = res.collect({ it = it + s[0] })
}
permutts.trampoline(s.substring(1), res) // s.substring(1) ~ list.tail()
}
}.trampoline()
例子:
permutts('VV', [''])
s = VV, res = []
s = V, res = [0, 1]
s = , res = [00, 10, 01, 11]
Result: [00, 10, 01, 11]
permutts('0V0', [''])
s = 0V0, res = []
s = V0, res = [0]
s = 0, res = [00, 01]
s = , res = [000, 010]
Result: [000, 010]
關於你的代碼,來自TrampolineClosure
javadoc:
TrampolineClosure包裹一個需要在功能蹦床上執行的閉包。 在調用時,TrampolineClosure將調用原始閉包等待其結果。 如果調用的結果是TrampolineClosure的另一個實例,可能是因為調用TrampolineClosure.trampoline()方法而創建的,則將再次調用TrampolineClosure。 返回的TrampolineClosure實例的重復調用將繼續,直到返回TrampolineClosure以外的值。 這個價值將成為蹦床的最終結果。
也就是說,尾調用優化中的替換。 在你的代碼中,只要其中一個TrampolineClosure沒有返回TrampolineClosure,整個TrampolineClosure
鏈就會返回。
從groovy 2.3開始,您可以使用@TailRecursive
AST轉換進行尾調用優化:
import groovy.transform.TailRecursive
@TailRecursive
List permutts(String s, List res = ['']) {
if (s.length() == 0) {
res
} else {
res = (s[0] == 'V') ? res.collect({ it = it + '0' }) + res.collect({ it = it + '1' }) : res.collect({ it = it + s[0] })
permutts(s.substring(1), res)
}
}
編輯 :
為了完成我的回答,上面的內容可以在功能折疊的一行中完成,在Groovy中注入 (使用集合的頭部作為初始值並迭代尾部):
assert ['000', '010'] == ['0', 'V', '0'].inject([''], { res, value -> (value == 'V') ? res.collect({ it = it + '0' }) + res.collect({ it = it + '1' }) : res.collect({ it = it + value }) })
assert ['00', '10', '01', '11'] == ['V', 'V'].inject([''], { res, value -> (value == 'V') ? res.collect({ it = it + '0' }) + res.collect({ it = it + '1' }) : res.collect({ it = it + value }) })
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.