【问题标题】:SML with lazy list function具有惰性列表功能的 SML
【发布时间】:2023-08-13 06:07:01
【问题描述】:

我正在尝试制作一个可以返回lazylist的特定第n个元素的函数。

这是我做的:

datatype 'a lazyList = nullList
                 | cons of 'a * (unit -> 'a lazyList)

fun Nth(lazyListVal, n) = (* lazyList * int -> 'a option *)
    let fun iterator (laztListVal, cur, target) =
        case lazyListVal of
             nullList => NONE
           | cons(value, tail) => if cur = target
                                  then SOME value
                                  else iterator (tail(), cur+1, target)
    in
        iterator(lazyListVal,1,n)
    end

我期望的结果是,随着recusing的进行,最终变量cur与变量target相同,然后函数迭代器返回 SOME value 所以它会返回最后的第 n 个元素。

但是当我编译并运行它时,它只返回第一个元素,但是我使用惰性列表对象进行测试。

请找出问题所在。我不知道...

cf) 我制作了另一个与此问题相关的函数,该函数将lazylist 转换为包含前N 个值的SML 原始列表。以上代码:

fun firstN (lazyListVal, n) = (* lazyList * int -> 'a list *)
    let fun iterator (lazyListVal, cur, last) =
        case lazyListVal of
             nullList => []
           | cons(value, tail) => if cur = last
                                  then []
                                  else value::iterator(tail(),cur+1,last)
    in
        iterator(lazyListVal,0,n)
    end          

奇怪的是函数 firstN 正常工作。

【问题讨论】:

  • firstN 有效,因为它没有Nth 的错字错误。它的参数lazyListVal 隐藏了其父级的同名参数,而Nth 将其自己的参数称为与其父级不同的参数,并随后引用其父级(列表的头部)而不是自己的。

标签: sml lazylist


【解决方案1】:

问题在于您的iterator 函数执行case lazyListVal of ...,但递归尾部被称为laztListVal,因此对于每次迭代,它都会查看第一个列表。使用更好的变量名来避免这种“隐形”错误。

nth 的更简单定义:

datatype 'a lazyList = NullList | Cons of 'a * (unit -> 'a lazyList)

fun nth (NullList, _) = NONE
  | nth (Cons (x, xs), 0) = SOME x
  | nth (Cons (_, xs), n) = nth (xs (), n-1)

val nats = let fun nat n = Cons (n, fn () => nat (n+1)) in nat 0 end

val ten = nth (nats, 10)

编辑:虽然函数模式匹配在这里是理想的,但您也可以在这里使用 case ... of ...。不过,辅助函数似乎没有必要,因为您可以简单地将输入参数 n 用作迭代器:

fun nth (L, n) =
    case (L, n) of
         (NullList, _) => NONE
       | (Cons (x, xs), 0) => SOME x
       | (Cons (_, xs), n) => nth (xs (), n-1)

但是,您可能希望使函数更健壮:

fun nth (L, n) =
    let fun nth' (NullList, _) = NONE
          | nth' (Cons (x, xs), 0) = SOME x
          | nth' (Cons (_, xs), n) = nth' (xs (), n-1)
    in if n < 0 then NONE else nth' (L, n) end

这里有一个辅助函数确保n &lt; 0 只检查一次。

(你也可以raise Domain,因为负索引没有明确定义。)

【讨论】:

  • 那么函数“firstN”没有出现同样问题的原因是什么?
  • 我尝试将case...of 语句转换为function pattern matching,令人惊讶的是,正如您所建议的那样,它可以正常工作。感谢您的帮助。
  • 不客气!但问题与函数模式匹配与 case ... of 无关。你可以让任何一个工作。我提供了一个使用 case ... of 的示例。