问题
Directions
Function
expandthat receives a list of any type and an integer numbern, and returns a list in which each item of input list is replicatedntimes. 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