问题
I want to mix values using two lists:
List1 : [3; 2; 8; 1; 9; 3; 6]
List2: [5; 7; 0]
Output : [3; 5; 2; 7; 8; 0; 1; 9; 3; 6]
I only found info about list with the same length what about this example when i have diffrent ?
The same length :
let rec mix =function
|(x::xs,y::ys) -> x::y::(mix (xs,ys))
|([],[]) -> []
| _ -> failwith "mix:param";;
mix ([3;2;8;1;9;3;6], [5; 7; 0]) ;;
回答1:
The best way to break this down is by considering each possible case for a particular step. You currently have:
|(x::xs,y::ys)
This is the case where each of the lists has at least one more item left|([],[])
This is the case where both lists are empty_
This case handles everything else.
So the cases you are missing are the cases where one list is empty and the other list has at least one item left. Those cases are |(x::xs,[])
and |([],y::ys)
. So add those two options to your match statement like so:
let rec mix =function
|(x::xs,[]) |([],y::ys) -> failwith "do something here"
|(x::xs,y::ys) -> x::y::(mix (xs,ys))
|([],[]) -> []
| _ -> failwith "mix:param"
You'll notice that you now get a warning indicating that the last case will never be matched so it can be removed like so:
let rec mix =function
|([],[]) -> []
|(x,[]) |([],x) -> failwith "test"
|(x::xs,y::ys) -> x::y::(mix (xs,ys))
Notice how I moved the base case to the top so it gets matched before (x,[])
or ([], x)
. Now all that is needed is code to handle those last two cases. It looks like this:
|(x,[]) |([],x) -> x
i.e. return the rest of the values in the list.
So the final solution looks like this:
let rec mix =function
|([],[]) -> []
|(x,[]) |([],x) -> x
|(x::xs,y::ys) -> x::y::(mix (xs,ys))
A further sneaky optimization you could make would be to remove the base case entirely since it will be covered by the second case. I.e (x,[])
also matches ([],[])
and will return []
as desired. This leaves us with just:
let rec mix =function
|(x,[]) |([],x) -> x
|(x::xs,y::ys) -> x::y::(mix (xs,ys))
回答2:
Step 1: understand the example
This isn't too hard if you have a good understanding what the example function does. Make sure that you know what the following mean; look them up if you are unsure (search web/Stackoverflow):
Recursion
Basic F# pattern matching
Cons
(::)
, both as an operator to create a list and as a patternF# list expressions
[...]
, again both to create lists and as a pattern
If you fully understand the original function, you only need to remove the case(s) you no longer need from the pattern match, and add the new cases: how to finish mixing when reaching the end of one list.
So, I recommend that you solve it yourself before reading the second part of this answer. The MSDN reference for F# is a good place to start. For example, see this page for rules and examples on pattern matching.
Step 2: Solution variant (read Step 1 first!)
Modifying the original function isn't all you can do in this case. Here's a proposal with some improvements:
let rec private mixAux acc = function
| h1 :: t1, h2 :: t2 -> mixAux (h2 :: h1 :: acc) (t1, t2)
| [], t | t, [] -> List.rev acc @ t
let mix l1 l2 = mixAux [] (l1, l2)
mix [3; 2; 8; 1; 9; 3; 6] [5; 7; 0] // yields [3; 5; 2; 7; 8; 0; 1; 9; 3; 6]
There are multiple techniques at work here which you might want to look up:
The line beginning with
| [], t | t, []
is an or pattern where two cases have only one body.t
is matched either one way or the other, but used in the same expression in both cases.The use of an accumulator,
acc
, helps to make the function tail recursive. Look up tail recursion if the function needs to work on long inputs or needs to be fast.The use of
private
hides the original "auxiliary" function and exposes a function that takes arguments in curried form, and doesn't need the accumulator argument when called.The code in the question does not follow common formatting conventions. For example, function bodies are usually indented and commas usually followed by a space. See for example this page on formatting conventions for a starting point on how many people format F#.
回答3:
Well, there are really only four cases, so here is a naive way of doing it:
let rec mx xs ys =
match (xs, ys) with
| (x::xs, y::ys) -> x:: y :: (mx xs ys)
| ([], y::ys) -> y :: (mx [] ys)
| (x::xs, []) -> x :: (mx xs [])
| ([], []) -> []
来源:https://stackoverflow.com/questions/27135302/mix-values-from-two-list-using-recursion