An implementation problem of F# Seq

前端 未结 2 1085
自闭症患者
自闭症患者 2021-02-14 02:09

I am digging into F# source code recently.

in Seq.fs:

// Binding. 
//
// We use a type defintion to apply a local dynamic optimization. 
// We automatic         


        
2条回答
  •  温柔的废话
    2021-02-14 02:43

    When yield! appears in a non-tail-call position, it essentiall means the same thing as:

    for v in  do yield v
    

    The problem with this (and the reason why is that quadratic) is that for recursive calls, this creates a chain of iterators with nested for loops. You need to iterate over the whole sequence generated by for every single element, so if the iteration is linear, you get a quadratic time (because the linear iteration happens for every element).

    Let's say the rwalk function generates [ 9; 2; 3; 7 ]. In the first iteration, the recursively generated sequence has 4 elements, so you'd iterate over 4 elements and add 1. In the recursive call, you'd iterate over 3 elements and add 1, etc.. Using a diagram, you can see how that's quadratic:

    x
    x x 
    x x x
    x x x x
    

    Also, each of the recursive calls creates a new instance of object (IEnumerator) so there is also some memory cost (although only linear).

    In a tail-call position, the F# compiler/librar does an optimization. It "replaces" the current IEnumerable with the one returned by the recursive call, so it doesn't need to iterate overe it to generate all elements - it is simply returned (and this also removes the memory cost).

    Related. The same problem has been discussed in the C# lanaugage design and there is an interesting paper about it (their name for yield! is yield foreach).

提交回复
热议问题