Adding 2 Int Lists Together F#

前端 未结 2 1369
清歌不尽
清歌不尽 2021-01-19 06:21

I am working on homework and the problem is where we get 2 int lists of the same size, and then add the numbers together. Example as follows.

    vecadd [1;2         


        
相关标签:
2条回答
  • 2021-01-19 06:43

    You can map over both lists together with List.map2 (see the docs) It goes over both lists pairwise and you can give it a function (the first parameter of List.map2) to apply to every pair of elements from the lists. And that generates the new list.

     let a = [1;2;3]
     let b = [4;5;6]
    
     let vecadd  = List.map2 (+)
    
     let result  = vecadd a b
     printfn "%A" result
    

    And if you want't to do more work 'yourself' something like this?

    let a = [1;2;3]
    let b = [4;5;6]
    
    let vecadd l1 l2 = 
        let rec step l1 l2 acc = 
            match l1, l2 with
                | [],  [] -> acc
                | [], _ | _, [] -> failwithf "one list is bigger than the other"
                | h1 :: t1, h2 :: t2 -> step t1 t2 (List.append acc [(h1 + h2)])
        step l1 l2 []
    let result  = vecadd a b
    printfn "%A" result
    

    The step function is a recursive function that takes two lists and an accumulator to carry the result.

    • In the last match statement it does three things
      • Sum the head of both lists
      • Add the result to the accumulator
      • Recursively call itself with the new accumulator and the tails of the lists
    • The first match returns the accumulator when the remaining lists are empty
    • The second match returns an error when one of the lists is longer than the other. The accumulator is returned as the result when the remaining lists are empty.

    The call step l1 l2 [] kicks it off with the two supplied lists and an empty accumulator.

    0 讨论(0)
  • 2021-01-19 06:59

    First, your idea about modifying the first list instead of returning a new one is misguided. Mutation (i.e. modifying data in place) is the number one reason for bugs today (used to be goto, but that's been banned for a long time now). Making every operation produce a new datum rather than modify existing ones is much, much safer. And in some cases it may be even more performant, quite counterintuitively (see below).

    Second, the way you're trying to do it, you're not doing what you think you're doing. The double-colon doesn't mean "modify the first item". It means "attach an item in front". For example:

    let a = [1; 2; 3]
    let b = 4 :: a    // b = [4; 1; 2; 3]
    let c = 5 :: b    // c = [5; 4; 1; 2; 3]
    

    That's how lists are actually built: you start with a empty list and prepend items to it. The [1; 2; 3] syntax you're using is just a syntactic sugar for that. That is, [1; 2; 3] === 1::2::3::[].

    So how do I modify a list, you ask? The answer is, you don't! F# lists are immutable data structures. Once you've created a list, you can't modify it any longer.

    This immutability allows for an interesting optimization. Take another look at the example I posted above, the one with three lists a, b, and c. How many cells of memory do you think these three lists occupy? The first list has 3 items, second - 4, and third - 5, so the total amount of memory taken must be 12, right? Wrong! The total amount of memory taken up by these three lists is actual just 5 cells. This is because list b is not a block of memory of length 4, but rather just the number 4 paired with a pointer to the list a. The number 4 is called "head" of the list, and the pointer is called its "tail". Similarly, the list c consists of one number 5 (its "head") and a pointer to list b, which is its "tail".

    If lists were not immutable, one couldn't organize them like this: what if somebody modifies my tail? Lists would have to be copied every time (google "defensive copy").

    So the only way to do with lists is to return a new one. What you're trying to do can be described something like this: if the input lists are empty, the result is an empty list; otherwise, the result is the sum of tails prepended with the sum of heads. You can write this down in F# almost verbatim:

    let rec add a b =
        match a, b with
        | [], [] -> []   // sum of two empty list is an empty list
        | a::atail, b::btail -> (a + b) :: (add atail btail)  // sum of non-empty lists is sum of their tails prepended with sum of their heads
    

    Note that this program is incomplete: it doesn't specify what the result should be when one input is empty and the other is not. The compiler will generate a warning about this. I'll leave the solution as an exercise for the reader.

    0 讨论(0)
提交回复
热议问题