【问题标题】:Standard ML Expand List标准 ML 展开列表
【发布时间】:2018-10-12 14:09:19
【问题描述】:

方向

函数expand 接收任意类型的列表和一个整数 编号n,并返回一个列表,其中输入列表的每个项目是 复制n 次。例如,展开 [1,2,3] 3 必须是 评估为 [1,1,1,2,2,2,3,3,3]。函数的类型必须是'a list→int→‘一个列表。

这是我的解决方案,我通过具有两个功能来绕过要求。当我移至列表中的下一项时,我正在努力将n 重置为原始值。我在我的实现中通过将原始的n 值保存到永远不会改变的s 来做到这一点。如何消除对s 的需求?

fun duplicate([], n, s) = [] |
    duplicate(l, n, s) = 
    if n > 1 then hd l::duplicate(l, (n-1), s)
    else hd l::duplicate(tl l, s, s);

fun expand([], n) = [] |
    expand(l, n) = duplicate(l, n, n);

【问题讨论】:

    标签: sml


    【解决方案1】:

    定义辅助函数不是“作弊”,这很好。
    这是一个更大的问题,你定义了一个错误类型的函数 - expand 的类型比你最终得到的函数的数量更重要(请注意,描述说明了“必须是”类型,但是并不是说你不能定义辅助函数)。

    您遇到问题是因为您试图一次“攻击”整个输入列表。
    当您遇到“对列表的每个元素执行 X”问题时,首先要做的是考虑“编写一个函数,用 one 执行 X,然后 List.map 它”。

    如果我们有一个可以重复k 次的函数,我们可以将它应用于每个列表元素。

    我们可以写repeat: int * 'a -> 'a list,但这需要一个数字和一个东西,而且在任何地方map 都不方便。
    如果我们可以动态“修复”数字并获得一个函数'a -> 'a list,那就太好了。
    如果您以方便的顺序给出参数,Currying 可以让您完全做到这一点。

    fun repeat 0 i = []
      | repeat n i = i :: repeat (n - 1) i;
    

    加载和测试:

    val repeat = fn : int -> 'a -> 'a list
    val it = () : unit
    - repeat 3 4;
    val it = [4,4,4] : int list
    

    目前看起来不错。
    我们现在可以编写repeat 4 并获得一个接受“某物”并重复四次的函数。

    让我们使用它:

    - fun expand xs n = List.map (repeat n) xs;
    val expand = fn : 'a list -> int -> 'a list list
    

    字体看起来不太好。让我们看看我们刚刚创建了什么。

    - expand [1,2,3] 3;
    val it = [[1,1,1],[2,2,2],[3,3,3]] : int list list
    

    几乎正确 - 列表应该是“扁平的”。

    幸运的是,List structure 有一个可以提供帮助的函数:concat: 'a list list -> 'a list,它获取列表列表并将它们附加在一起,因此我们可以将结果传递给它:

    - fun expand xs n = List.concat (List.map (repeat n) xs);
    val expand = fn : 'a list -> int -> 'a list
    

    这样看起来更好。

    - expand [1,2,3] 3;
    val it = [1,1,1,2,2,2,3,3,3] : int list
    

    【讨论】:

    • 很高兴知道concat。你的高阶函数真的很优雅。我的很粗鲁XD
    【解决方案2】:

    这是我的解决方案,我通过有两个函数来绕过要求。

    这不是问题。在编写标准 ml 时使用 helper 函数很好。特别是这里的辅助函数是尾递归,它将优化堆栈帧。有时(不是这里)嵌套的模式是一种替代方法,但是由于没有尾递归,因此需要更多的内存。

    如何消除对 s 的需求?

    我不知道你为什么不喜欢额外的s。但我确实有一个解决方案:使用闭包隐式保存。

    简洁风格:

    fun expand [] n =  []
        | expand (node::list) n =
          let   fun n_times f n x = if n = 0 then x else f (n_times f (n-1) x) 
          in    n_times (fn list => case list of node::list => node::[node]) n [node] @ expand list n end
    

    更具可读性的风格:

    fun expand [] n =  []
      | expand (node::list) n =
        let
            fun n_times f n x =
                if n = 0
                then x
                else f (n_times f (n-1) x)
        in
            n_times (fn list => case list of node::list => node::[node]) n [node]
            @
            expand list n
        end
    

    这个闭包 (fn list => case list of node::list => node::[node]) 不包括额外的s,但它要做的是帮助n_times 完成n 次缺点。我想这就是你想要的

    顺便说一句,expand [1,2,3] 3‘a list→int→‘a list 的要求要求您使用 curry 函数而不是元组/非 curry 函数(标准 ml 中的所有参数只是一个元组)。所以,我上面给出的解决方案使用了 curry function(expand [] n and n_times f n x) 。您也可以使用一个帮助函数将非柯里函数转换为柯里函数:

    fun curry_helper f x y z = f (x, y, z) 
    

    如果有不明白的地方欢迎评论。

    【讨论】:

      【解决方案3】:

      这是一个使用标准库函数的解决方案:

      fun expand (xs, n) =
          List.concat (List.map (fn x => List.tabulate (n, fn _ => x)) xs)
      

      还有一个:

      fun expand (xs, n) =
          List.foldr (fn (x, acc) => List.tabulate (n, fn _ => x) @ acc) [] xs
      

      【讨论】:

        猜你喜欢
        • 2015-04-05
        • 1970-01-01
        • 2017-11-30
        • 2011-05-05
        • 2014-04-02
        • 2017-11-23
        • 2012-04-14
        • 2020-07-07
        • 2019-08-02
        相关资源
        最近更新 更多