libro - ¿Alguna forma de iterar una tupla en swift?
fahrenheit 451 ray bradbury (5)
¡Sí tu puedes!
func iterate<C,R>(t:C, block:(String,Any)->R) {
let mirror = reflect(t)
for i in 0..<mirror.count {
block(mirror[i].0, mirror[i].1.value)
}
}
¡Y voilá!
let tuple = ((false, true), 42, 42.195, "42.195km")
iterate(tuple) { println("/($0) => /($1)") }
iterate(tuple.0){ println("/($0) => /($1)")}
iterate(tuple.0.0) { println("/($0) => /($1)")} // no-op
Tenga en cuenta que la última no es una tupla, por lo que no ocurre nada (aunque es una 1-tupla o "Única" a la que se puede acceder al contenido .0
, la reflect(it).count
es 0).
Lo que es interesante es que iterate()
puede iterar incluso otros tipos de colección.
iterate([0,1]) { println("/($0) => /($1)") }
iterate(["zero":0,"one":1]) { println("/($0) => /($1)") }
¡Y esa colección incluye class
y struct
!
struct Point { var x = 0.0, y = 0.0 }
class Rect { var tl = Point(), br = Point() }
iterate(Point()) { println("/($0) => /($1)") }
iterate(Rect()) { println("/($0) => /($1)") }
Advertencia: el valor pasado como el segundo argumento del bloque es tipo Any
. Tienes que devolverlo a los valores con tipo original.
Tengo curiosidad por cómo hacer un bucle for con una tupla en Swift.
Sé que para acceder a cada miembro puede usar notación de puntos usando el número de índice
var tupleList = ("A",2.9,3,8,5,6,7,8,9)
for each in tupleList {
println(each)
}
// Error: el tipo no se ajusta a la secuencia del protocolo
No, no puedes. La razón es que no se requiere que todos los elementos de la tupla tengan el mismo tipo, por lo que no podría saber qué tipo debería tener each
.
Swift actualmente no es compatible con iterar sobre tuplas.
Las principales razones son:
- No hay forma en tiempo de ejecución para determinar la cantidad de elementos en una tupla
- No hay forma de acceder a un elemento en un índice específico a excepción de los
tupleList.0
tiempo de compilación comotupleList.0
. Realmente querría un subíndicetupleList[0]
pero eso no se nos proporciona
Francamente, no puedo ver una razón por la que usarías una tupla en lugar de una matriz si quieres iterar sobre ella.
No tiene sentido iterar sobre una tupla porque:
- Las tuplas siempre tienen una longitud fija y cada elemento tiene un tipo fijo
- Puede nombrar a cada miembro de la tupla con un nombre que pueda usar para acceder a él más tarde
Las matrices están bien hechas para iterar sobre:
- Longitud arbitraria
- Puede almacenar múltiples tipos usando una superclase común o AnyObject
- Se puede declarar como un literal de manera similar a las tuplas:
var list = ["A",2.9,3,8,5,6,7,8,9]
La excelente solución de @dankogai , actualizada para Swift 3.0:
func iterate<Tuple>(_ tuple:Tuple, body:(_ label:String?,_ value:Any)->Void) {
for child in Mirror(reflecting: tuple).children {
body(child.label, child.value)
}
}
El uso sigue siendo idéntico a los ejemplos de @dankogai (más allá de println()
→ print()
renombrar de Swift 2) .
Tenga en cuenta que la etiqueta ahora es de tipo String?
cuando era antiguamente String
, para hacer coincidir el cambio de tipo de MirrorType.subscript(…).0
de Swift 1 con Mirror.Child.label
Swift 3. Sin embargo, para las tuplas sin label
la label
arg vuelve a aparecer como ".0"
, ".1"
, ".2"
, etc.- es solo nil
para algunos otros tipos.
Además, me tomé la libertad de cambiar el nombre de tipos y argumentos para adaptar mejor los estándares de nomenclatura solidificados de Swift 3 , y cambiar el tipo de retorno de cierre a Void
.
Nota: noté que alguien me votó negativamente aquí. No puedo imaginar por qué, aparte del argumento (justo) de que construir la funcionalidad de la aplicación alrededor de la reflexión en Swift es piratear el sistema tipográfico, y es probable que conduzca a un código malicioso (las tuplas de Swift no deberían no se considera un tipo de datos abstracto, sino más bien una pequeña colección de variables, similar al método args). Como contraargumento, al principio terminé portando esto a Swift 3 en el proyecto porque lo necesitaba, para una mejor description
y debugDescription
description
. Porque una salida de depuración correcta le ahorrará horas y horas de frustración. ;-) Además, esto podría ser realmente útil para pruebas unitarias ... porque las pruebas en última instancia están más interesadas en "¿el resultado de esta operación coincidió con lo que esperamos?"