4

For class I have to make a program that calculates the birthday problem Now I'm having trying to learn kotlin at the same time and I'm having trouble with a little snippet of code:

        val checkSet = mutableSetOf<Int>()
        generateSequence{ Random.nextInt(n)}.forEach {
            if(!checkSet.add(it)) {
                return@outForeach
            }
        }
        outForeach@
        sum += checkSet.size

As you can see I'm trying to do this with an infinite sequence. Kotlin doesn't accept this as outForeach is an unresolved reference. But this doesn't work either:

        val checkSet = mutableSetOf<Int>()
        generateSequence{ Random.nextInt(n)}.forEach {
            if(!checkSet.add(it)) {
                return@forEach
            }
        }
        sum += checkSet.size

This will just start the forEach loop again. Is there a way to implement something as a forEachUntil or so?

p.s. I'm aware that this looks a lot like this question: 'return' doesn't jump out of forEach in Kotlin It's just that I don't really get the answers and I don't know if its applicable here. Also a way to implement forEachUntil seems for me to be far more elegant

Typhaon
  • 828
  • 8
  • 27

2 Answers2

2

Alternatives you may want to consider instead of first:

  • using a simple while without body:

    while (checkSet.add(Random.nextInt(n))); // <- that semicolon is required! otherwise you execute what is coming next within the while
    
  • using run with a label:

    run outForeach@{
      generateSequence{ Random.nextInt(n)}.forEach {
        if(!checkSet.add(it)) {
          return@outForeach
        }
      }
    }
    
  • maybe also takeWhile might be helpful. In this specific case however it is surely not (as it would check against the checkSet and leave us with a sequence that isn't consumed... but if the condition would be different, it may make sense to consider something like take, takeWhile, takeLast, etc.):

    generateSequence { Random.nextInt(n) }
        .takeWhile(checkSet::add) // as said: for this specific condition it doesn't make sense... 
        .forEach { /* do nothing except consume the sequence */ } // the same values you added to the set would be available in this step of course
    
Roland
  • 22,259
  • 4
  • 57
  • 84
  • the while loop is definitely a more elegant solution as it produces no unused sequence or reference. – Dean Dec 12 '18 at 15:14
  • that's true and while it works quite well for this specific example it may not in other cases where a real ~return is wanted.... (although I don't know any such cases that really require a goto-label-construct, that aren't also solveable using other means) – Roland Dec 12 '18 at 15:21
1

I think I found the solution myself:

        val checkSet = mutableSetOf<Int>()
        generateSequence{ Random.nextInt(n)}.first { !checkSet.add(it) }
        sum += checkSet.size

Basically use the function first() and keep returning false until you want to get out of the loop. And just drop the return of the function first()

Typhaon
  • 828
  • 8
  • 27