这是我的第一个SML程序。我正在尝试编写一个函数,以列表形式将第一个数字返回到第n个Hofstadter的女性或男性序列。到目前为止我所拥有的是:
val m = fn (n) => if n = 0 then 1 :: [] else m f (n - 1);
val f = fn (n) => if n = 0 then 0 :: [] else f m (n - 1);
您可以在此处了解序列: https://en.wikipedia.org/wiki/Hofstadter_sequence#Hofstadter_Female_and_Male_sequences
我得到的错误是:
[opening sequence.sml]
sequence.sml:1.49 Error: unbound variable or constructor: f
sequence.sml:1.47-1.58 Error: operator is not a function [tycon mismatch]
operator: int list
in expression:
(m <errorvar>) (n - 1)
val it = () : unit
我该如何纠正?
答案 0 :(得分:3)
我最终采用了这种方法:
fun
m (n) = if n = 0 then 0 else n - (f (m (n - 1)))
and
f (n) = if n = 0 then 1 else n - (m (f (n - 1)));
val seq = fn n => List.tabulate((n), f);
这很慢。如果有人有更快的版本,那么我很乐意看到它。
答案 1 :(得分:1)
虽然您已经修复了它们,但您的原始方法存在两个问题:
在SML中,函数应用程序是左关联的,因此m f (n - 1)
被解释为(m f) (n - 1)
,而不是所需的m (f (n - 1))
。您可以通过明确指定包围m (f (n - 1))
。
要从f
m
和 m
拨打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! *)
和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 ...
等),但我选择不这样做,因为在SML中编写递归函数的语法要紧凑得多。
为了概括这一点,您可以使用哈希表之类的东西替换memoizing的数组版本。然后我们就不必预先指定memoization的大小。