0

I am converting old Swift 2 code to Swift 3 and I am facing difficulty in converting following for loop

for (var nSize = merkleTree.count; nSize > 1; nSize = (nSize + 1) / 2)
{
    //...
}

There are many similar question on SO but I didn't find any solution applicable to my problem Or I didn't understand.

I thought that below code will work but it is giving error.

for var nSize in merkleTree.count.stride(to:1, by:(nSize+1)/2)

Use of unresolved identifier 'nSize'

Varun Naharia
  • 5,318
  • 10
  • 50
  • 84
  • 1
    Have a look at https://stackoverflow.com/questions/40070202/express-for-loops-in-swift-with-dynamic-range for some generic solutions to replace "arbitrary" C-style for loops. – Martin R Jan 02 '18 at 10:36

1 Answers1

2

I don't think this can be written using for anymore, but you can use while loop to get the job done:

var nSize = merkleTree.count
while nSize > 1 {
    // loop body

    nSize = (nSize + 1) / 2
}

I would expect stride not to work in this case, because as your error states, you cannot use nSize as the stride parameter - nSize is iterating variable that gets declared based on the range, so you need the range to exist. At least that's my interpretation of the error (I know that theoretically you can generate range based on the previously generated item, but obviously stride does not work that way).

I believe you can find a way to generate a proper array of values using reduce (because I was able to, see below, maybe you can make it simpler), or by implementing your own stride that would accept a closure instead of a step (which would allow you to compute next item based on previous one), but both approaches are more complicated and obscure than using the simple while loop, so I personally prefer the while loop.

My not so nice reduce implementation (in result it uses an array and not a range, since by looking at NSRange I don't think you can create a range that does not step by 1):

let merkleTree = [1,2,3,4,5,6,7,8,9]

let numberOfDivisions = Int(log2(Double(merkleTree.count))) + 1
let startValue = merkleTree.count
let nSizes = (0..<numberOfDivisions).reduce([startValue]) { (result, next) -> [Int] in
    var newResult = result
    newResult.append((result.last! + 1) / 2)
    return newResult
}
print(nSizes)
// and now you can for-in it:
for nSize in nSizes {
    // ...
}
Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90