[英]Recursive C# function returns from inside a for-loop - how to translate to F#?
很長一段時間C#開發人員,學習F#。 我選擇了一個功能相當的C#代碼,我將其作為一個學習練習 - 將其翻譯為F#。 我已經做了很多關於函數式編程的閱讀,並定期使用C#中的函數結構,但我只有幾個小時的學習F#。
此功能是解決類似於“LonPos 101”的難題的程序的一部分,您可以在亞馬遜等上找到該解決方案。解算器中使用的策略基於識別拼圖空間中只有30個有效位置,因此“迄今為止的解決方案”可以用一個整數表示,每個部分的每個有效位置也可以用一個整數表示,一個完整的解決方案是一個包含7個部分中每一個的可能位置的集合,其中“權重” “7件中加入溶液重量(2 ^ 30-1)。 在給定的函數中,“key”是該片段的“主鍵”,wbk是“按鍵加權” - 按鍵索引,包含相應片段的有效位置列表,而“w”是上述“解決方案” -至今”。 返回值是從鍵到選定位置的映射,並且在導致解決方案的成功遞歸的出口路徑上填充。 我發現在開發C#解決方案時,使這個排序列表使解決方案查找器的速度提高了一個數量級,但它與普通列表同樣有效。
這是我遇到問題的C#函數:
int solutionWeight;
Dictionary<int,int> Evaluate(int w, Dictionary<int, SortedSet<int>> wbk, int key)
{
if (w == solutionWeight)
return new Dictionary<int, int>();
if (key == 8)
return null;
foreach (var w2 in wbk[key])
{
if ((w & w2) != 0)
continue;
var s = Evaluate(w | w2, wbk, key + 1);
if (s != null)
{
s.Add(key, w2);
return s;
}
}
return null;
}
這是我對F#版本的嘗試 - 它編譯,但它無法正常工作 - 當執行w不是solutionWeight並且key等於8的情況時,它最終會在let ss = ...行中失敗並導致KeyNotFoundException失敗。對我來說,為什么這行代碼甚至在這種情況下被執行是沒有意義的,但......
let rec Evaluate(w:int, wbk:Dictionary<int, SortedSet<int>>, key:int):Dictionary<int,int> =
if w = solutionWeight then
Dictionary<int,int>()
else if key = 8 then
null
else
// ... this is wrong - runs off the end of some collection - fails with key not found exception
let ws = wbk.[key] |> Seq.filter (fun w2 -> (w2 &&& w) = 0)
/// ... for some reason, execution resumes here after the key = 8 clause above
let ss = ws |> Seq.map (fun w -> (w,Evaluate(w, wbk, key+1)))
let sw = ss |> Seq.find (fun sw -> snd sw <> null)
let s = snd sw
s.Add(key, fst sw)
s
指出我正確的方向! 我的下一個嘗試是首先以更實用的方式重寫C#代碼,但感覺這個版本即將開始工作(雖然可能仍然遠離慣用的F#)。
更新:
我重新編寫了F#函數,通過使用一對相互遞歸的函數來消除循環。 它確實有效,但它比非互相遞歸的C#版本慢約2倍。
let rec Evaluate(w:int, wbk:Dictionary<int, SortedSet<int>>, key:int):Dictionary<int,int> =
if w = solutionWeight then
Dictionary<int,int>()
else if key = 8 then
null
else
EvalHelper(w, wbk, key, wbk.[key].GetEnumerator())
and EvalHelper(w:int, wbk:Dictionary<int, SortedSet<int>>, key:int, ws:IEnumerator<int>):Dictionary<int,int> =
if ws.MoveNext() then
let w2 = ws.Current
if (w &&& w2) = 0 then
let s = Evaluate(w ||| w2, wbk, key+ 1)
if s <> null then
s.Add(key, w2)
s
else
EvalHelper(w, wbk, key, ws)
else
EvalHelper(w, wbk, key, ws)
else
null
我該如何進一步改進它?
更新:我更多地調整它 - 感覺更多F#ish,但我仍然覺得我應該能夠擺脫更多類型的注釋並更多地使用F#native類型。 這是一項正在進行中的工作。
let rec Evaluate(w, wbk:Dictionary<int, SortedSet<int>>, key):Dictionary<int,int> option =
let rec EvalHelper(ws) =
match ws with
| w2 :: mws ->
match w &&& w2 with
| 0 ->
let s = Evaluate(w ||| w2, wbk, key+ 1)
match s with
| None -> EvalHelper(mws)
| Some s ->
s.Add(key, w2)
Some(s)
| _ -> EvalHelper(mws)
| _ ->
None
if w = solutionWeight then
Some (Dictionary<int,int>())
else if key = 8 then
None
else
EvalHelper(List.ofSeq wbk.[key])
翻譯此函數的關鍵是將for循環轉換為遞歸,如第一次更新中所示。
let rec Evaluate(w:int, wbk:Dictionary<int, SortedSet<int>>, key:int):Dictionary<int,int> =
if w = solutionWeight then
Dictionary<int,int>()
else if key = 8 then
null
else
EvalHelper(w, wbk, key, wbk.[key].GetEnumerator())
and EvalHelper(w:int, wbk:Dictionary<int, SortedSet<int>>, key:int, ws:IEnumerator<int>):Dictionary<int,int> =
if ws.MoveNext() then
let w2 = ws.Current
if (w &&& w2) = 0 then
let s = Evaluate(w ||| w2, wbk, key+ 1)
if s <> null then
s.Add(key, w2)
s
else
EvalHelper(w, wbk, key, ws)
else
EvalHelper(w, wbk, key, ws)
else
null
隨后的更新剛剛清理了一下風格 - 將它推向了慣用的F#。
let rec Evaluate(w, wbk:Dictionary<int, SortedSet<int>>, key):Dictionary<int,int> option =
let rec EvalHelper(ws) =
match ws with
| w2 :: mws ->
match w &&& w2 with
| 0 ->
let s = Evaluate(w ||| w2, wbk, key+ 1)
match s with
| None -> EvalHelper(mws)
| Some s ->
s.Add(key, w2)
Some(s)
| _ -> EvalHelper(mws)
| _ ->
None
if w = solutionWeight then
Some (Dictionary<int,int>())
else if key = 8 then
None
else
EvalHelper(List.ofSeq wbk.[key])
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.