首页 > 解决方案 > 在 SML 中查找 int 列表的模式以及它在没有库函数的情况下出现的位置

问题描述

我试图找到最常出现的模式或值。我想要一个类似的功能:

mode:' 'a list -> (''a * int) list

它返回模式及其发生的位置,除非有平局,否则返回所有出现的情况,例如:

mode([1,1,2,3,5,8]) ===> [(1,2)]
mode([1,3,5,2,3,5]) ===> [(3,2),(5,2)]
mode([true,false,true,true]) ====>[(true,3)]

我试图在没有 SML 中的库函数的情况下做到这一点。

到目前为止,我得到了:

fun mode(L)=
if null L then nil
else if hd L= hd (tl L) then 1+mode(hd(tl L))
else mode(tl L);

我知道这不对我想我很好奇你们如何保持模式发生位置和模式是什么的索引并将它们作为列表中的元组返回。

标签: recursionsmlsmlnjmlstatistical-mode

解决方案


您正在尝试通过几个更简单的练习来解决许多部分的练习。从你目前的进度来看,你是否考虑过解决一些非常相似的练习来达到最终目标?在解决编程问题时,这通常是一个很好的建议:将您当前的问题简化为更简单的问题并解决这些问题。

我会先尝试解决这个问题

  • histogram : ''a list -> (''a * int) list在列表的元素上构建一个:

    fun histogram [] = ...
      | histogram (x::xs) = ...
    

    通过将每个x与它的插入count到直方图中来做到这一点。

    fun insert (x, []) = ...
      | insert (x, (y, count) :: hist) = ...
    

    并编写一些可以不时执行的测试。

  • 查找mode : ''a list -> ''a列表的 :

    fun mode xs = ... (histogram xs)
    

    通过在直方图中找到计数最多的元素来做到这一点:

    fun findMax [] = ... (* what if the list/histogram is empty? *)
      | findMax [(x, count)] = ...
      | findMax ((x, count) :: (y, count) :: hist) = ...
    

并最终尝试解决这个问题

  • 当您很好地掌握了递归地表示和导航常规直方图时,您可以创建一个注释histogram : (''a * int * int list) list,它不仅包含每个元素的频率,还包含它们在输入列表中的位置:

    fun histogram_helper ([], _) = ...
      | histogram_helper (x::xs, i) = ... histogram_helper (xs, i+1) ...
    

    通过将每个x及其count位置i以及先前找到的位置is插入直方图中来做到这一点:

    fun insert (x, i, []) = ...
      | insert (x, i, (y, count, is) :: hist) = ...
    
  • 查找mode : ''a list -> (''a * int list) list列表的(可能是多个):

    fun mode xs = ... (histogram xs)
    

    通过在直方图中找到计数最多的(可能是多个)元素来做到这一点:

    fun findMax ([],                     countMax, tmpModes) = ...
      | findMax ((x, count, is) :: hist, countMax, tmpModes) = ...
    

    countMax : int在 中重复的频率tmpModes : (''a * int * int list) list。这里countMaxtmpModes正在累积结果参数。通过确定是否(x, count, is)应该丢弃以支持所有tmpModes,或者应该添加它tmpModes,或者应该选择它来支持所有来做到这一点。tmpNodes

    我很好奇你们如何保持模式发生位置和模式是什么的索引,并将它们作为列表中的元组返回。

    是的,这不是微不足道的。使用我建议的子问题划分,回答这个取决于我们是在histogram函数还是findMax函数中:

    您可以将索引存储为histogram包含元素和频率的元组的一部分。在findMax中,由于您可能会收集多个结果,因此您需要跟踪哪个频率最高 ( countMax) 以及选择的临时模式是什么 ( tmpModes);在以后的递归调用中进行替换或添加。

    所以回答你的问题:在一个累积参数中。


以及对您的代码片段的一些反馈

fun mode(L)=
if null L then nil
else if hd L= hd (tl L) then 1+mode(hd(tl L))
else mode(tl L);

使用模式匹配代替null,hdtl

fun count_4s [] = 0
  | count_4s (x::xs) = (if x = 4 then 1 else 0) + count_4s xs

fun count_ns ([],    _) = 0
  | count_ns (x::xs, n) = (if x = n then 1 else 0) + count_ns (xs, n)

fun count_12 ([], ones, twos) = (ones, twos)
  | count_12 (x::xs, ones, twos) =
      if x = 1 then count_12 (xs, ones+1, twos) else
      if x = 2 then count_12 (xs, ones, twos+1) else
      count_12 (xs, ones, twos)

fun count_abc ([], result) = result
  | count_abc (x::xs, ((a, ca), (b, cb), (c, cc))) =
      count_abc (xs, if x = a then ((a, ca+1), (b, cb), (c, cc)) else
                     if x = b then ((a, ca), (b, cb+1), (c, cc)) else
                     if x = c then ((a, ca), (b, cb), (c, cc+1)) else
                     ((a, ca), (b, cb), (c, cc)))

构建直方图是对此的一种扩展,而不是固定值4,或者固定数量的onestwos,你有一个完整的列表,你必须动态查找你拥有的那个x,,并确定是否需要将其添加到直方图中或在直方图中递增。

最好的方法是在辅助函数中做到这一点,例如,如果count_abc使用辅助函数,

fun insert_abc (x, ((a, ca), (b, cb), (c, cc))) =
    if x = a then ((a, ca+1), (b, cb), (c, cc)) else
    if x = b then ((a, ca), (b, cb+1), (c, cc)) else
    if x = c then ((a, ca), (b, cb), (c, cc+1)) else
    ((a, ca), (b, cb), (c, cc)))

fun count_abc ([], result) = result
  | count_abc (x::xs, result) =
      count_abc (xs, insert (x, result))

仅代替直方图表示

(''a * int) * (''a * int) * (''a * int)

你要

(''a * int) list

并且insert应该是递归的,而不是如何insert_abc重复的。


推荐阅读