-1

In Swift, the c-style for-loop is deprecated, so I am switching to the stride function.

In the first function, I use a c-style for-loop. In the second function, I used the stride function. createNumbers output is [8, 7, 6, 5, 4, 3, 2] createNumbers2 output is [8, 7, 6, 5, 4] I am trying to get the first output.

That means the stride function will ignore if I change the endIdx value. Is there a way to work around this, using stride, such that createNumbers2 also prints 3 and 2?

func createNumbers(lastNum: Int) -> [Int] {
    var endIdx = lastNum
    var answer : [Int] = []
    for var i = 8; i >= endIdx; i -= 1 {
        answer.append(i)
        if i == 6 {
            endIdx /= 2
        }
    }
    return answer
}

func createNumbers2(lastNum: Int) -> [Int] {
    var endIdx = lastNum
    var answer : [Int] = []
    for i in 8.stride(through: endIdx, by: -1) {
        answer.append(i)
        if i == 6 {
            endIdx /= 2
        }
    }
    return answer
}

print(createNumbers(4)) // [8, 7, 6, 5, 4, 3, 2]
print(createNumbers2(4)) // [8, 7, 6, 5, 4]
  • 3
    What's the real world usage of that? – Wain Mar 27 '16 at 20:29
  • 1
    `ignore` is potentially the wrong word here... the stride function is already done doing its work well before the loop body has entered for even the first time. It just generates and returns a sequence basically... – nhgrif Mar 27 '16 at 20:33
  • 2
    It is a plus that one can no longer do what the example is trying. With `stride` right at the top you know the sequence, no tricks. For tricky stuff use `while`. – zaph Mar 27 '16 at 20:39
  • Or implement your own sequence/generator. – nhgrif Mar 27 '16 at 20:42
  • @nhgrif Please don't, that just makes the code more difficult for the next guy to figure out. Pease write code for the next developers who may not be as cleaver. – zaph Mar 27 '16 at 20:43
  • Brian Wilson Kernaghan: "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." But what does he know. – zaph Mar 27 '16 at 20:45
  • 2
    ... there are plenty of good ways to implement clearly understandable sequences & generators. There are also plenty of ways to write confusing code that is difficult for the next guy to figure out, most of them have nothing to do with sequences or generators... I'd consider rethinking that 100%-anti-generator attitude, @zaph – nhgrif Mar 27 '16 at 20:45
  • I'm not 100%-anti-generator but it had be a better solution with a clear name. – zaph Mar 27 '16 at 20:47
  • I agree. But I'd apply that rule to everything. There's nothing inherently confusing about well-written generators, and there's nothing inherently clear about poorly-written everything-else. – nhgrif Mar 27 '16 at 20:48
  • I also agree, apply to everything. I do see a lot of "look at the new shiny, gotta use it". – zaph Mar 27 '16 at 20:50
  • 2
    The fact that the real-world purpose of this is so difficult to discern suggests that this code is inherently bad. If the language prevents you from writing (technically correct) bad code, then good on the language. – BallpointBen Mar 27 '16 at 21:31
  • 1
    The problem is that you are trying to write bad code in a language which is trying to prevent you from using exactly this form of bad code. Rethink your approach. We can help you but you will have to show a real use case. – Sulthan Mar 27 '16 at 21:51
  • Thanks everyone. Sorry about the real-world purpose confusion. My actual code was much more complicated, because the whole thing was inside a second loop, so I simplified it to the question at hand. But I agree my code was not good practice. – swifter_user Mar 28 '16 at 21:52

1 Answers1

2

The stride method...

Returns the sequence of values (self, self + stride, self + stride + stride, ... last) where last is the last value in the progression less than or equal to end.

So, as you noted, when you invoke the stride here

for i in 8.stride(through: endIdx, by: -1) {

the sequence is generated. Changing endIdx after this line will NOT change the sequence the for in will use.

Solution

Let's look at the logic you want to execute. Maybe we can find a better implementation.

Please correct if I am wrong.

You want to input an integer lastNum

IF lastNum > 6 THEN OUTPUT 8,7

ELSE OUTPUT 8,7,... [lastNum/2]

Right?

Then you can write it this way

func numbers(lastNum: Int) -> [Int] {
    let biggest = 8
    guard lastNum <= biggest else { return [] }
    let smallest = lastNum > 6 ? lastNum : lastNum / 2
    return [Int](smallest...biggest).reverse()
}

Test

numbers(4) // [8, 7, 6, 5, 4, 3, 2]

More tests

createNumbers(-1) == numbers(-1) // true
createNumbers(0) == numbers(0) // true
createNumbers(1) == numbers(1) // true
createNumbers(2) == numbers(2) // true
createNumbers(3) == numbers(3) // true
createNumbers(4) == numbers(4) // true
createNumbers(5) == numbers(5) // true
createNumbers(6) == numbers(6) // true
createNumbers(7) == numbers(7) // true
createNumbers(8) == numbers(8) // true
createNumbers(9) == numbers(9) // true

Personal considerations

This is just my point of view. I know the for in construct is still perfectly available in Swift. However building a value (an array in this case) using a for in does force us to use mutable values and imperative code. We should avoid both as much as possible because immutable values are safer and declarative code is more clear and potentially faster.

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • Understood. I see that stride is actually generating the sequence, that won't change later. After seeing the comments, next time I should not mutate the values of a for-loop. – swifter_user Mar 28 '16 at 21:41