Standard ML Expand List

烂漫一生 提交于 2019-12-11 10:45:51

问题


Directions

Function expand that receives a list of any type and an integer number n, and returns a list in which each item of input list is replicated n times. For example, expand [1,2,3] 3 must be evaluated to [1,1,1,2,2,2,3,3,3].The type of the function must be ‘a list→int→‘a list.

Here's my solution where I cheated around the requirements a bit by having two functions. What I am struggling with is resetting n to the original value when I move on to the next item in the list. I do so in my implementation by saving the original n value to s which is never changed. How do I go about removing the need for 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);

回答1:


Defining helper functions isn't "cheating", it's good.
It's a bigger problem that you defined a function with the wrong type - the type of expand is more important to the exercise than the number of functions you end up with (note that the description says what the type "must be", but not that you're not allowed to define helper functions).

You're having problems because you're trying to "attack" the whole input list at once.
When you have a "do X with each element of a list" problem, the first thing to do is to think "write a function that does X with one thing and then List.map it".

If we had a function that could repeat something k times, we could apply that to each list element.

We could write repeat: int * 'a -> 'a list, but that needs both a number and a thing and is inconvenient to map anywhere.
It would be nice if we could "fix" the number dynamically and get a function 'a -> 'a list.
Currying lets you do exactly that, if you give the parameters in a convenient order.

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

Load and test:

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

Looks good so far.
We can now write repeat 4 and get a function that takes "something" and repeats that four times.

Let's use it:

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

The type doesn't look great. Let's see what we just created.

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

Almost correct - the list should be "flat".

Luckily, the List structure has a function that helps: concat: 'a list list -> 'a list, which takes a list of lists and appends them together, so we can just pass the result along to that:

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

That looks better.

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



回答2:


Here's my solution where I cheated around the requirements a bit by having two functions.

This is not a problem. Using helper function is good when writing standard ml. Particularly, the helper function here is tail recursion, which will optimize stack frame. Sometimes(not here) nested case for a pattern is an alternative, but the cost is more memory required because of no tail recursion.

How do I go about removing the need for s?

I don't know why don't you like the extra s. But I do have a solution: use closure to save it implicitly.

compact style:

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

More readable style:

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

This closure (fn list => case list of node::list => node::[node]) doesn't include extra s, but what it to do is help n_times to complete n times cons. I think this is what you want

BTW, the requirements expand [1,2,3] 3 and ‘a list→int→‘a list require you to use curry function instead of tuple/non-curry function(all arguments in standard ml is just one tuple) . So, the solution I give above uses curry function(expand [] n and n_times f n x) . You can also use one help function to convert non-curry function into curry function:

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

Feel free to comment if somewhere you don't understand.




回答3:


Here's a solution that uses standard-library functions:

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

And here is another:

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


来源:https://stackoverflow.com/questions/52731226/standard-ml-expand-list

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!