我想创建一个简单的SML程序,该程序从左到右遍历一个列表。假设我有N个K项不同类型的项的列表。例如,列表1 3 1 3 1 3 3 2 2 1
有10个数字{ 1}}类型。
我想做的是从左到右浏览此列表,当我找到所有K个不同的数字时停止。在这种情况下,我会在碰到前2个后立即停止。
这可以通过在每个步骤中将头部和尾部的列表分开并处理head元素来完成。但是如何跟踪所找到的不同数字呢?
这可以在C / C ++中完成,只需持有一个计数器和一个包含K个元素的布尔数组即可。如果我偶然发现一个元素3(1,2,3)
,我就将其设为bool[i]=false
。
虽然说数组不是SML的最佳选择,所以我想知道我是否必须使用另一个数据结构,或者是否必须创建一个新函数来检查每次我是否以前看过此元素(这会花费时间的复杂性。
答案 0 :(得分:2)
如何跟踪找到的不同数字?
[...] 在C / C ++中,由 [...] 一个具有K个元素的布尔数组
抽象地,我将您想要的数据结构称为位集。
我会给你两个答案,一个使用稀疏容器,另一个使用位集。
我将使用一个列表来跟踪您已经看到的元素:
fun curry f x y = f (x, y)
val empty = []
fun add x set = curry op:: x set
fun elem x set = List.exists (curry op= x) set
fun seen k xs =
let fun seen_ 0 _ _ = true
| seen_ _ [] _ = false
| seen_ k (x::xs) set =
if elem x set
then seen_ k xs set
else seen_ (k-1) xs (add x set)
in seen_ k xs empty end
您也可以使用平衡二叉树作为集合类型;这样可以将查找次数减少为 O(lg n)。使用实际容器(列表或树)而不是位数组的优点是sparse arrays/matrices的优点。这适用于''a list
s。
[...] 具有K个元素的布尔数组 [...]
如果我偶然发现了元素 i [...]
到目前为止,您还没有说过元素始终是从0到 K-1 的无符号整数,如果它们应该由数组中的唯一索引表示,则这是一个要求长度 K 。
SML具有一个称为Word
/ word
的模块/类型,用于无符号整数(字)。添加此约束后,输入列表的类型应为word list
,而不是''a list
。
当您使用许多命令式,已编译的语言创建原始类型的数组时,您会变得可变unboxed arrays。 SML的Array类型也是可变的,但是这种数组中的每个bool
都将被装箱。
获取不可变,未装箱的位数组的一种简单方法是对IntInf
(SML / NJ; {{3 }});它会随着翻转而自动增长。可能看起来像:
fun bit x = IntInf.<< (1, x)
val empty = IntInf.fromInt 0
fun add x set = IntInf.orb (set, bit x)
fun elem x set = IntInf.> (IntInf.andb (set, bit x), 0)
函数seen
相同。
k
递归减少而set
动态增长这一事实意味着您不仅限于 [0,K-1] 范围内的元素,大小为 K 的数组就是这种情况。
示例用法:
- seen 5 [0w4, 0w2, 0w1, 0w9];
val it = false : bool
- seen 5 [0w1, 0w2, 0w3, 0w4, 0w8];
val it = true : bool
如果元素很大,此解决方案将使用大量内存:
- seen 1 [0w100000000];
*eats my memory slowly*
val it = true : bool
您可以做的其他事情:
structure BitSet = struct ... end
,该模块用操作empty
,add
和elem
封装一个抽象类型,隐藏特定的实现(无论它是{{1} }或IntInf.int
或bool Array.array
)。''a list
,该函数提取fun fold_until f e xs = ...
的递归方案,以便避免手动递归;常规seen_
是不够的,因为它一直持续到列表为空。您可以使用implementations vary或使用异常来构建它。