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