遍历列表直到满足特定条件

时间:2019-03-28 19:44:06

标签: list functional-programming sml smlnj

我想创建一个简单的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的最佳选择,所以我想知道我是否必须使用另一个数据结构,或者是否必须创建一个新函数来检查每次我是否以前看过此元素(这会花费时间的复杂性。

1 个答案:

答案 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,该模块用操作emptyaddelem封装一个抽象类型,隐藏特定的实现(无论它是{{1} }或IntInf.intbool Array.array)。
  • 创建一个函数''a list,该函数提取fun fold_until f e xs = ...的递归方案,以便避免手动递归;常规seen_是不够的,因为它一直持续到列表为空。您可以使用implementations vary或使用异常来构建它。
  • 考虑error-aware return type