Hofstadter女性和男性序列在SML

时间:2017-05-10 02:42:43

标签: sequence sml smlnj ml sequences

这是我的第一个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

我该如何纠正?

2 个答案:

答案 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)

虽然您已经修复了它们,但您的原始方法存在两个问题:

  1. 在SML中,函数应用程序是左关联的,因此m f (n - 1)被解释为(m f) (n - 1),而不是所需的m (f (n - 1))。您可以通过明确指定包围m (f (n - 1))

  2. 来解决此问题
  3. 要从f m m拨打f,您需要使用关键字{{在第一个声明上使用{}}代替fun(使函数递归),在第二个声明上使用关键字val代替andfun函数与第一个函数相互递归)。这看起来像

    val
  4. 为了让它更快,你可以记住!诀窍是让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重命名为mfemale。已记忆的malefMemo分为gMemofemale male。有趣的是,如果我们致电memoizeMutual,那么 male'male'的结果都会被记忆。

    要确认它确实更快,请尝试评估female'。它比你的版本要永远快得多。

    最后一点,唯一的递归函数是hofstadter 10000fMemo。我写的每个其他函数都可以写成匿名函数(gMemoval memoizeMutual = fn ...等),但我选择不这样做,因为在SML中编写递归函数的语法要紧凑得多。

    为了概括这一点,您可以使用哈希表之类的东西替换memoizing的数组版本。然后我们就不必预先指定memoization的大小。