雖然你已經修復這些問題,有兩個問題您原始的方法:
功能的應用程序是左關聯的SML所以m f (n - 1)
被解釋爲(m f) (n - 1)
,不是期望m (f (n - 1))
。您可以通過明確指定包圍m (f (n - 1))
來解決此問題。
爲了能夠從m
和m
從f
叫f
,您需要在第一個聲明中使用關鍵字fun
,而不是val
(使函數的遞歸),以及關鍵字and
,而不是fun
或val
對第二個聲明(使函數與第一個函數相互遞歸)。這看起來像
fun f n = ... (* I can call f or m from here! *)
and m n = ... (* I can call f or m from here! *)
爲了使其更快,你可以memoize的!訣竅是讓f
和m
作爲自己的記憶版本的參數。
(* Convenience function: Update arr[i] to x, and return x. *)
fun updateAndReturn arr i x = (Array.update (arr, i, SOME x); x)
(*
* Look up result of f i in table; if it's not found, calculate f i and
* store in the table. The token is used so that deeper recursive calls
* to f can also try to store in the table.
*)
fun memo table f token i =
case Array.sub (table, i)
of NONE => updateAndReturn table i (f token i)
| SOME x => x
(*
* Given f, g, and n : int, returns a tuple (f', g') where f' and g' are memoized
* versions of f and g, respectively. f' and g' are defined only on the domain
* [0, n).
*)
fun memoizeMutual (f, g) n =
let
val fTable = Array.array (n, NONE)
val gTable = Array.array (n, NONE)
fun fMemo i = memo fTable f (fMemo, gMemo) i
and gMemo i = memo gTable g (gMemo, fMemo) i
in
(fMemo, gMemo)
end
fun female _ 0 = 1
| female (f, m) n = n - m (f (n - 1))
fun male _ 0 = 0
| male (m, f) n = n - f (m (n - 1))
fun hofstadter upTo =
let
val (male', female') = memoizeMutual (male, female) upTo
in
(List.tabulate (upTo, male'), List.tabulate (upTo, female'))
end
我改名爲f
和m
到female
和male
。記憶的fMemo
和gMemo
通過female
和male
通過memoizeMutual
進行線程化。有趣的是,如果我們撥打male'
,male'
和female'
的結果被記錄下來。
要確認它確實速度更快,請嘗試評估hofstadter 10000
。它比你的版本要快得多。
作爲最後一點,唯一的遞歸函數是fMemo
和gMemo
。我寫的每一個函數都可以寫成一個匿名函數(val memoizeMutual = fn ...
,val female = fn ...
等),但我選擇不這樣做,因爲在SML中寫遞歸函數的語法要緊湊得多。
爲了概括這一點,你可以用類似哈希表的東西來替換memoizing的數組版本。然後,我們不必先指定備忘錄的大小。
非常感謝您發佈和解釋您的解決方案。 「記憶」對我來說是一個新的過程。我期待着在未來實施它。感謝您的時間。 –