DEV Community

Cover image for IEnumerables transferred to Golang
David Kröll
David Kröll

Posted on

IEnumerables transferred to Golang

There is no doubt that the IEnumerable type from C# is very powerful.
It's especially effective when the data we would like to iterate has yet to be aquired or calculated.

To state a matching example, the fibonacci sequence is one of them.
There we have a sequence with numbers depending on the numbers beforehand. So the next number is the sum of the two previous ones. I guess everyone is familiar with this sequence - here it is for numbers up to 100:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
Enter fullscreen mode Exit fullscreen mode

When calculating this sequence and storing it using an Array datatype, we would have to generate these numbers and allocate memory.
Furthermore, we would have to figure out how many fibonacci numbers we would like to generate beforehand,
because the fibonacci sequence is basically infinite, but our Array is not.

Of course, one could do this without allocating a new Array, but then you have to implement the generation code in the same method.

static void Main(string[] args)
{
    // a loop for printing the numbers
    foreach (var x in GetNumbersArray(12))
    {
        if (x > 100)
            break;
        Console.WriteLine(x);
    }
}

static int[] GetNumbersArray(int i)
{
    int[] result = new int[i];

    var (a, b) = (0, 1);

    // and another loop for generating the numbers
    for (var counter = 1; counter < i; counter++)
    {
        // logic without allocating a new array
        // would have to be implemented here
        (a, b) = (a + b, a);
        result[counter] = a;
    }

    return result;
}
Enter fullscreen mode Exit fullscreen mode

Here the C# IEnumerable type helps out. It combines the best of the two worlds. No extra memory has to be allocated and the computation runs as we request the next item. In addition, no loops takes place more than once (as it is the case with the Array).

So the solution using the IEnumerable would look like this:

static void Main(string[] args)
{
    // request the next number by iterating
    foreach (var x in GetNumbers())
    {
        // break the loop when we reach more than 100
        if (x > 100)
            break;
        Console.WriteLine(x);
    }
}


static IEnumerable<int> GetNumbers()
{
    var (a, b) = (0, 1);

    while (true)
    {
        (a, b) = (a + b, a);
        // return the next number as soon as requested
        // using the yield statement
        yield return a;
    }
}
Enter fullscreen mode Exit fullscreen mode

No memory gets allocated to store the sequence, and the caller of the GetNumbers() could decide when to break the loop.

Translating to go

In Go we do not have something like the IEnumerable in C#, in fact we have no chance to create a custom type that allows us to be iterated by implementing an interface. In C# we could achieve this by using the IEnumerator interface. Of course we do not have to reinvent the wheel, everything we have to do is rebuild the interface from scratch using custom types and access methods.

type Fibonacci struct {
    a int
    b int
}

// Get return the current value to print
func (f *Fibonacci) Get() int {
    return f.a
}

// Next calculates the next value
// and returns false if there are no more values
func (f *Fibonacci) Next() bool {
    f.a, f.b = f.a+f.b, f.a
    return true
}

// NewFibonacci initializes a new Fibonacci type
func NewFibonacci() Fibonacci {
    return Fibonacci{
        a: 0,
        b: 1,
    }
}

func main() {
    sequence := NewFibonacci()

    for sequence.Next() {
        if x := sequence.Get(); x < 100 {
            fmt.Println(x)
        } else {
            break
        }
    }

}
Enter fullscreen mode Exit fullscreen mode

This is now the same way as C# IEnumerator is implemented under the hood. We are able to iterate using the Next() method and access the current value using the Get()method. It perfectly fits our needs and satisfies our requirements. No additional memory has to be allocated and no loop is executed twice. Using this blueprint, every usecase for IEnumerable can be solved in Go. In fact, it is already in use in Go's standard library.

One example implementation would be the bufio.Scanner type - https://pkg.go.dev/bufio#Scanner

Discussion (2)

Collapse
klvenky profile image
Venkatesh KL

A nice example. I'm not familiar with C# but that looks relatable. Would you mind if i reference this article & create another article with same context but in reference to JavaScript??

Collapse
davidkroell profile image
David Kröll Author

No, I am, absolute looking forward to this! Thanks for your kudos.