1

I would like to filter a lazy structure and then reduce it using Swift language.

func main() -> () {
    let result = (1...)
        .lazy
        .filter { $0 < 3 }
        .reduce(0, {$0 + $1})
    return print(
        result
    )
}

main()

This code compiles; however, the program doesn't execute in a proper way (takes too long). The printed result on the screen should be 3. Is there a way to accomplish this goal ?

F. Zer
  • 1,081
  • 7
  • 9

2 Answers2

1

The issue is not that your sequence is lazy, it's that your sequence is infinite. You might be looking for the sequence method. Example:

let s = sequence(first: 0) {
    $0 > 3 ? nil : $0 + 1
}
let result = s.reduce(0) { $0 + $1 }
print(result) // 10

s is lazy and is potentially infinite but not actually infinite, because the method that generates the next item in the series does an early exit by returning nil when the sequence goes past 3. This is similar to your filter except that it is not a filter, it's a stopper. You can use any condition you like here to generate the stopper.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • or a regular for loop with a break. `for x in 1... {` `if x < 3 {` `result += 1` `} else {` `break` `}` `}` – Leo Dabus Feb 14 '21 at 02:17
  • Thank you so much, @matt ! I understood the concept. By the way, I bought you book "AppleScript: The Definitive Guide" a while ago and learned a lot :-) – F. Zer Feb 14 '21 at 16:29
  • 1
    Well _that_ brings back memories! :) – matt Feb 14 '21 at 18:47
0

To get the program to terminate, you would need to add an upper limit on your original sequence (1...).

As it's written, you have an infinite sequence of numbers, starting at 1. The following operators — the filter, in particular — have no way of "knowing" that they can discard the rest of the sequence once they pass 3. They have to process the entire infinite sequence, filtering out all but the first two elements, before your reduce can produce a final result and you can print it out. (In practice, you'll eventually overflow Int, so the program would terminate then, but that's not really a good thing to rely on.)

If you don't want to change the original (1...), you can approximate the same behavior by swapping out your filter with a prefix. A filter has to look at every element; a prefix can "know" that it stops after a certain number of elements. For example, this runs very quickly and prints out 3:

let result = (1...)
    .lazy
    .prefix(2)
    .reduce(0) {$0 + $1}
Tim
  • 59,527
  • 19
  • 156
  • 165
  • Thank you, @Tim. So, there is no way to reduce a lazy structure ? The problem is: I do not know beforehand how many elements will remain after the filter. – F. Zer Feb 14 '21 at 01:46
  • 1
    @F.Zer And neither does the compiler and neither does the runtime. This has nothing to do with the fact that the sequence is lazy; it has to do with the fact the sequence is infinite. It's totally unclear what the goal is supposed to be here. – matt Feb 14 '21 at 02:09