switch for example swift for-loop range

example - ¿Una forma concisa de no ejecutar un bucle ahora que C-Style para bucles se va a eliminar de Swift 3?



switch swift 4 (4)

Imagina que tenemos este código que funciona perfectamente para n >= 0 .

func fibonacci(n: Int) -> Int { var memo = [0,1] for var i = 2; i <= n; i++ { memo.append(memo[i-1] + memo[i-2]) } return memo[n] }

Si elimino el C-style for loop debido a los próximos cambios en Swift 3.0 , obtengo algo como esto:

func fibonacci(n: Int) -> Int { var memo = [0,1] for i in 2...n { memo.append(memo[i-1] + memo[i-2]) } return memo[n] }

Si bien esto funciona bien para n >= 2 , falla para los números 0 y 1 con este mensaje de error:

error fatal: no se puede formar el rango con el extremo <inicio

¿Cuál es la forma más concisa de corregir este código para que funcione correctamente para 0 y 1 ?

(Nota: está bien, e incluso es deseable, que los números negativos bloqueen la aplicación).

Nota: me doy cuenta de que podría agregar una declaración de guardia:

guard n >= 2 else { return memo[n] }

... pero espero que haya una mejor manera de arreglar solo la parte defectuosa del código ( 2...n ).

Por ejemplo, si hubiera una forma concisa de crear un rango que devuelva cero elementos si end < start , esa sería una solución más ideal.


Como resultado, la variable i siempre será igual al count de la matriz de memorización, por lo que puedes usarla como tu condición de ciclo:

func fibonacci(n: Int) -> Int { var memo = [0,1] while n >= memo.count { memo.append(memo[memo.count-1] + memo[memo.count-2]) } return memo[n] }

Alternativamente, podría expresar el bucle como una función recursiva:

func fibonacci(n: Int) -> Int { var memo = [0,1] func rec(i: Int) -> Int { if i >= memo.count { memo.append(rec(i-2) + rec(i-1)) } return memo[i] } return rec(n) }

Realmente, sin embargo, if es la mejor solución aquí. Range s no permiten que el extremo sea más pequeño que el comienzo por diseño. La línea adicional para:

func fibonacci(n: Int) -> Int { if n < 2 { return n } var memo = [0,1] for i in 2...n { memo.append(memo[i-1] + memo[i-2]) } return memo[n] }

Es legible y comprensible. (En mi opinión, el código anterior es mejor que el de la versión for ;; )


Puede crear fácilmente un rango válido con la función max() :

for i in 2 ..< max(2, n+1) { memo.append(memo[i-1] + memo[i-2]) }

Esto evalúa a un rango vacío 2 ..< 2 si n < 2 .

Es importante usar el operador ..< que excluye el límite superior porque 2 ... 1 no es un rango válido.

Pero en esta función, simplemente trataría primero los casos especiales

func fibonacci(n: Int) -> Int { // Let it crash if n < 0: precondition(n >= 0, "n must not be negative") // Handle n = 0, 1: if n <= 1 { return n } // Handle n >= 2: var memo = [0,1] for i in 2 ... n { memo.append(memo[i-1] + memo[i-2]) } return memo[n] }

(Tenga en cuenta que su matriz de memo está configurada en el valor inicial [0, 1] para cada llamada de función, por lo que los valores no son realmente "memorizados". Sin memoria no necesita una matriz, sería suficiente para mantener los dos últimos números para calcular el siguiente.)


La respuesta de @ Marc es genial: https://.com/a/34324032/1032900

Pero la sintaxis de zancada es demasiado larga para el uso frecuente, así que la hice un poco más agradable para los usos comunes de i ++ ...

extension Strideable { @warn_unused_result public func stride(to end: Self) -> StrideTo<Self> { return stride(to: end, by: 1) } } extension Strideable { @warn_unused_result public func stride(thru end: Self) -> StrideThrough<Self> { return stride(through: end, by: 1) } }

Entonces usa esto:

for i in startPos.stride(to: endPos) { print("pos at: /(i)") }


Para hacer esto de una manera que funcione para n <2, puede usar el método stride .

let startIndex = 2 let endIndex = n for i in stride(from: startIndex, through: endIndex, by: 1) { memo.append(memo[i-1] + memo[i-2]) }