SML - 在不使用 List.nth 的情况下从列表中获取特定元素



>im试图学习SML ATM的基础知识,偶然发现了一个我找不到答案的任务。

它是编写一个函数,该函数接受一个 int 和一个列表,在给定 int 的索引上返回列表中的特定元素。如您所见,它与 List.nth((-函数完全相同。

现在我很好奇。这就是我走了多远,但我只是想不出手动定位特定索引的方法。

fun nth(nil, _)     = 0
| nth(x::xs, 0)   = x;
| nth(x::xs, y)   = 
val list = [1, 2, 3];
nth(list, 0);

正如 John 所建议的,为空列表编制索引可能会引发异常,而不是返回 0。这使得nth适用于任何类型的列表,而不仅仅是 0 可以合理地认为"无结果"的int list的子集。似乎该函数缺少递归,无法适用于任何超过 0 的索引。下面是一个要使用的模板:

fun nth ([], _) = raise Empty
| nth (x::_, 0) = x
| nth (_::xs, n) = ...

这里添加了一个例外,并且不会在函数的每种情况下使用的变量已用伪变量_清空。您可能还需要信息更丰富的错误消息。

fun nth ([], n) = raise Fail "Failed to find the appropriate index!"
| nth (x::_, 0) = x
| nth (_::xs, n) = ...

一个"更安全"的nth版本具有'a list * int -> 'a option类型,即对于nth (xs, i),如果xs有一个i个元素x,它返回SOME x,如果没有,它返回NONE

fun nth_safe ([], _) = NONE
| nth_safe (x::_, 0) = SOME x
| nth_safe (_::xs, n) = ...

它"更安全",因为如果列表不够长,它不会引发异常。一个对抗性的例子:nth ([0,1,2], 3)

但如果指数为负数,它仍然无法处理。一个对抗性的例子:nth ([0,1,2], ~1)

您可以使用if n < 0 then ...在第三个函数体的...中解决这个问题,但随后会在每个递归步骤中执行,即使您很可能只需要检查一次。

此函数的可靠版本在向它传递负索引时引发错误。否则,您的函数可能会导致负循环,直到内存不足,因为递归情况(第 3 种情况(不会收敛到两种基本情况(情况 1 和 2(。对于基于异常的版本,您可以编写:

exception IndexError of int
fun nth (xs, n) =
let fun go ([], _) = raise IndexError n
| go (x::_, 0) = x
| go (_::ys, i) = ...
in if n < 0 then raise IndexError n else go (xs, n)
end

使用错误感知数据类型的健壮版本可能如下所示:

fun nth (xs, n) =
let fun go ([], _) = NONE
| go (x::_, 0) = SOME x
| go (_::ys, i) = ...
in if n < 0 then NONE else go (xs, n)
end

使用捕获索引错误的感知数据类型的健壮版本,就像具有自定义IndexError异常的基于异常的版本一样,如下所示:

datatype ('a, 'b) either = Left of 'a | Right of 'b
fun nth (xs, n) =
let fun go ([], _) = Left n
| go (x::_, 0) = Right x
| go (_::ys, i) = ...
in if n < 0 then Left n else go (xs, n)
end
val example_1 = nth ([2,3,5], 5)  (* gives: Left 5  *)
val example_2 = nth ([2,3,5], ~1) (* gives: Left ~1 *)
val example_3 = nth ([2,3,5], 2)  (* gives: Right 5 *)

一个简单的方法:

fun nth (nil,0) = raise Fail "You are out of bounds with nth element"
| nth ((x::xr),n) = if n=0 then x else nth (xr,(n-1))

最新更新