0
votes

The question is, how to create a python like iterator in Kotlin.

Consider this python code that parses string into substrings:

def parse(strng, idx=1):
    lst = []
    for  i, c in itermarks(strng, idx):
        if c == '}':
            lst.append(strng[idx:i-1])
            break
        elif c == '{':
            sublst, idx = parse(strng, i+1)
            lst.append(sublst)
        else:
            lst.append(strng[idx:i-1])
            idx = i+1
    return lst, i

>>>res,resl = parse('{ a=50 , b=75 , { e=70, f=80 } }')
>>>print(resl)
>>>[' a=50', ' b=75', [' e=7', ' f=80'], '', ' f=80']

This is a play example just to illustrate a python iterator:

def findany(strng, idx, chars):
    """ to emulate 'findany' in kotlin """
    while idx < len(strng) and strng[idx] not in chars:
        idx += 1
    return idx

def itermarks(strng, idx=0):
    while True:
        idx = findany(strng, idx, ',{}"')
        if idx >= len(strng):
            break
        yield idx, strng[idx]
        if  strng[idx] == '}':
            break
        idx += 1

Kotlin has iterators and generators, and as I understand it there can only be one per type. My idea is to define a type with a generator and instance that type. So the for loop from 'parse' (above) would look like this :

for((i,c) in IterMarks(strng){ ...... }

But how do i define the generator, and what is the best idiom.

2

2 Answers

0
votes

Kotlin uses two interfaces: Iterable<T> (from JDK) and Sequence<T>. They are identical, with the exception that the first one is eager, while the second one is lazy by convention.

To work with iterators or sequences all you have to do is implement one of those interfaces. Kotlin stdlib has a bunch of helper functions that may help.

In particular, a couple of functions for creating sequences with yield was added in 1.1. They and some other functions are called generators. Use them if you like them, or implement the interfaces manually.

0
votes

OK, after some work, here is the Kotlin for the iterator:

import kotlin.coroutines.experimental.*

fun iterMarks(strng: String, idx:Int=0)=buildSequence{
    val specials = listOf("\"", "{", "}", ",")
    var found:Pair<Int,String>?
    var index = idx
    while (true){
        found = strng.findAnyOf(specials, index)
        if (found == null) break
        yield (found)
        index= found.first + 1
    }
}

The main discoveries were that an iterator can be returned by any function, so the there is no need to add the iterator methods to an existing object. The JetBrains doco is solid, but lacks examples so hopefully the above example helps. You can also work from the basics, and again the notes are good but lack examples. I will post more on other approaches if there is interest.

The this code for 'parse' then works:

fun parse(strng:String, idxIn:Int=1): Pair<Any,Int> {
    var lst:MutableList<Any> = mutableListOf()
    var idx = idxIn
    loop@ for (mark in iterMarks(strng, idx)){
        if(mark==null ||mark.first <= idx){
            // nothing needed
        }
        else
        {
            when( mark.second ) {
                "}" -> {
                    lst.add(strng.slice(idx..mark.first - 1))
                    idx = mark.first + 1
                    break@loop
                }
                "{" -> {
                    val res: Pair<Any, Int>
                    res = parse(strng, mark.first + 1)
                    lst.add(res.first)
                    idx = res.second
                }

                "," -> {
                    lst.add(strng.slice(idx..mark.first - 1))
                    idx = mark.first + 1
                }
            }

        }
    }
    return Pair(lst, idx)

}

Hopefully this example will make it less work for the next person new to Kotlin, by providing an example of implementing an iterator. Specifically if you know how to make an iterator in python then this example should be useful