Iterable

In Shim, an “iterable” is a value that has an .iter() method that returns an iterator. Lists, Ranges, and Dicts are a few of the built-in iterables. Structs can also define their own iterators.

An “iterator” is a value with a .next() method that returns a value. When the iterator has no more values to return it should return StopIteration.

Typically you would use an iterator in a for loop.

let lst = [0, 1, 2]
for item in lst {
    print(item)
}
Press the play button to execute

You can see how that unrolls by calling .iter and .next explicitly

let lst = [0, 1, 2]
let iter = lst.iter()
print(iter.next())
print(iter.next())
print(iter.next())
print(iter.next())
Press the play button to execute

Builtin Iterables

These builtin types are iterable:

  • Lists - Yields each item in the list in order
  • Dicts - Yields each key in the dict in insertion order. Use .values() to get just the values or .items() to get key-value pairs.
  • Sets - Yields each value in the set in insertion order
  • Strings - Yields each character in the string
  • Ranges - Yields each value in the range (not including the end point)

List Example

for item in ["foo", None, 42] {
    print(item)
}
Press the play button to execute

Dict Example

let d = {"foo": "bar", 42: true}
for key in d {
    print(key)
}
for value in d.values() {
    print(value)
}
for key, value in d.items() {
    print("\(key): \(value)")
}
Press the play button to execute

Set Example

let s = {"foo", "bar", 42, true}
for value in s {
    print(value)
}
Press the play button to execute

String Example

let s = "Hello!"
for c in s {
    print(c)
}
Press the play button to execute

Range Example

for x in 1..5 {
    print(x)
}
Press the play button to execute

Builtin Functions

There are a number of builtin functions that operate on iterables:

  • map(iter, fn) - Returns a List of items where each item yielded by iter is transformed by the provided function
  • filter(iter) - Returns a List of truthy items yielded by the iterable
  • filter(iter, pred) - Returns a List of items where the function pred returns a truthy value
  • average(iter) - Returns the sum of the items in the iterable, divided by the count (or 0 if empty)
  • enumerate(iter) - Returns a List of tuples where the first item is the index and the second item is the value

Map Example

fn double(x) {
    x * 2
}
print(map([1, 2, 3], double))
Press the play button to execute

Filter Example

fn is_big(x) {
    x > 5
}
print(filter([0, 5, 10, 15, 20]))
print(filter([0, 5, 10, 15, 20], is_big))
Press the play button to execute

Average Example

print(average([]))
print(average([0, 1, 2]))
Press the play button to execute

Enumerate Example

let instruments = ["trumpet", "flute", "piano"]
for idx, instrument in enumerate(instruments) {
    print("\(idx): \(instrument)")
}
Press the play button to execute

Custom Iterators

Structs can define their own iteration methods. Here we define an iterable struct (a struct that has an .iter() method) that returns an iterator (a struct that has a .next() method).

struct CountDownIterator {
    num
    fn next(self) {
        let ret = if self.num >= 0 {
            self.num
        } else {
            StopIteration
        }
        self.num -= 1

        ret
    }
}

struct CountDown {
    fn iter(self) {
        CountDownIterator(5)
    }
}
for item in CountDown() {
    print(item)
}
Press the play button to execute

Shim provides a convenient builtin Iterator to make this a bit cleaner so that you don’t need to define a new iterator object separate from your iterable struct.

struct CountDown {
    fn iter(self) {
        let num = 5
        Iterator(fn() {
            let ret = if num >= 0 {
                num
            } else {
                StopIteration
            }
            num -= 1

            ret
        })
    }
}
for item in CountDown() {
    print(item)
}
Press the play button to execute