Mix values from two List using recursion

戏子无情 提交于 2019-12-11 01:39:33

问题


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:

  1. |(x::xs,y::ys) This is the case where each of the lists has at least one more item left
  2. |([],[]) This is the case where both lists are empty
  3. _ 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 pattern

  • F# 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

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