使用foldl / foldr插入函数

时间:2018-03-08 01:01:08

标签: sml ml mosml

我一直在研究一个单独的函数,它返回一个列表,在列表l的每个k元素之后插入元素x(从中计算) 列表的结尾)。例如,单独的(1,0,[1,2,3,4])应返回[1,0,2,0,3,0,4]。我完成了这个功能并使其工作如下:

fun separate (k: int, x: 'a, l: 'a list) : 'a list = 
  let
    fun kinsert [] _ = []
      | kinsert ls 0 = x::(kinsert ls k)
      | kinsert (l::ls) i = l::(kinsert ls (i-1))
  in
     List.rev (kinsert (List.rev l) k)
  end 

我现在尝试使用foldl / foldr简化函数而不进行任何递归,但我似乎无法使其正常工作。有关如何处理此问题的任何提示/建议?谢谢!

1 个答案:

答案 0 :(得分:1)

这些或多或少是我尝试使用foldl / foldr编写函数时的想法:

  • foldl / foldr从构成最终结果的逻辑中抽象出列表递归。

  • 首先绘制一个与原始程序结构非常相似的函数,但使用foldrkinsert而不是递归函数的函数是给{的函数{1}}:

    foldr

    这不是必须的; fun separate (k, x, L) = let fun kinsert (y, ys) = ... in foldr kinsert [] L end 也可能是匿名的。

  • 您正在使用内部帮助函数kinsert,因为您需要kinsertk)的副本,然后逐渐递减并重置为i每当它达到0时,k吐出的列表相当于折叠的累积变量,kinsert 暂时累积(偶尔重置) )以同样的方式。

  • 更改i的累积变量,为kinsert腾出空间:

    i

    现在折叠的结果变为fun separate (k, x, L) = let fun kinsert (y, (i, xs)) = ... in foldr kinsert (?, []) L end ,这会导致两个问题:1)我们只想暂时累积'a * 'a list ,但它的一部分最后的结果。这可以通过使用i丢弃它来规避。 2)如果结果现在是元组,我不确定将第一个#2 (foldr ...)替换为i

  • 由于?是一个单独的函数声明,您可以使用模式匹配和多个函数体:

    kinsert
  • 您的原始fun separate (k, x, L) = let fun kinsert (y, (0, ys)) = ... | kinsert (y, (i, ys)) = ... in ... foldr kinsert ... L end 偏离折叠以一种方式执行的递归模式:在中间模式中,当kinserti匹配时,您不是从0切掉一个元素,否则会折叠你的元素。因此,ls案例与原始案例略有不同;你可能会遇到一个错误的错误。

  • 请记住,0实际上会先访问列表中的最后一个元素,此时foldr将具有其初始值,其中原始i为初始值当你在第一个元素时,kinsert就会出现。

  • 根据您使用i还是foldl,您会遇到不同的问题:foldr会反转您的列表,但会按正确的顺序处理项目。 foldl会保持列表顺序正确,但在foldr未划分k的长度时会创建不同的结果...

  • 此时,请考虑使用L并反转列表:

    foldl

    否则,您会开始注意到fun separate (k, x, L) = let fun kinsert (y, (?, ys)) = ... | kinsert (y, (i, ys)) = ... in rev (... foldl kinsert ... L) end 可能应该separate (2, 0, [1,2,3,4,5])而不是[1,2,0,3,4,0,5]