Obtener el enésimo carácter en Swift: cómo no perder rendimiento

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Magia de cadenas en Swift: por qué "simplemente tomar un carácter" es un desafío

\n\n

Cualquier desarrollador que haya migrado a Swift desde Python, JavaScript o C++, tarde o temprano se topa con un muro. Una tarea aparentemente simple: "Dame el tercer carácter de la cadena". En Python es str[2]. En JavaScript es str[2]. En Swift... es más complicado. Y si alguna vez ha buscado la respuesta a la pregunta "get nth character of a string in Swift", sabrá que Stack Overflow está literalmente repleto de esta pregunta (miles de visitas, cientos de votos).

\n\n

¿Por qué? Porque Swift trata las cadenas no como una matriz de bytes, sino como una secuencia compleja de grafemas compatible con Unicode. Y si se aborda la tarea de frente, el rendimiento puede caer y el código puede volverse ilegible.

\n\n

En este artículo no solo daremos la respuesta. Analizaremos la optimización para obtener el enésimo carácter en Swift, veremos el rendimiento de diferentes enfoques y elegiremos el mejor. ¡Allá vamos!

\n\n

¿Por qué string[2] no funciona? Breve introducción a String.Index

\n\n

En Swift, String no es una colección de Character con acceso aleatorio. Es una colección con acceso a través de índices, que no son números enteros. ¿Por qué es así?

\n\n
    \n
  • Unicode y grafemas. Un solo "carácter" puede constar de varios escalares Unicode (por ejemplo, el emoji "🇷🇺" o "é" con acento). Si el índice fuera simplemente un Int, entonces str[1] podría devolver la mitad de un emoji.
  • \n
  • Rendimiento. Almacenar el índice como un Int para una cadena Unicode es un compromiso. Swift utiliza acceso O(1) al inicio y al final, pero para un índice arbitrario es necesario recorrer la cadena.
  • \n
\n\n

Por lo tanto, la forma estándar de obtener el enésimo carácter en Swift se ve así:

\n\n
let str = "¡Hola, Swift!"\nlet index = str.index(str.startIndex, offsetBy: 2)\nlet char = str[index] // "o"
\n\n

¿Funciona? Sí. ¿Es óptimo? No.

\n\n

El problema del rendimiento: qué esconde offsetBy

\n\n

Cada llamada a str.index(startIndex, offsetBy: n) es una operación O(n). Imagine que tiene una cadena de 10,000 caracteres y quiere obtener el carácter en el índice 9999. Swift recorrerá los 9999 caracteres para encontrar el deseado. ¿Hace esto en un bucle 100 veces? Obtiene una complejidad cuadrática.

\n\n

Aquí hay un ejemplo de código lento que suelen usar los principiantes:

\n\n
let longString = "A" + String(repeating: "b", count: 10_000) + "C"\nfor i in 0..<longString.count {\n    let index = longString.index(longString.startIndex, offsetBy: i)\n    let char = longString[index]\n    // Hacer algo con char\n}
\n\n

Este código funcionará muy lentamente, porque cada llamada a offsetBy comienza desde startIndex. De hecho, la complejidad es O(n²).

\n\n

Optimización n.º 1: Uso de índice incremental

\n\n

La forma más sencilla de acelerar get nth character Swift es no recalcular el índice desde cero cada vez. Use index(after:) o formIndex(after:).

\n\n
let longString = "A" + String(repeating: "b", count: 10_000) + "C"\nvar index = longString.startIndex\nfor _ in 0..<longString.count {\n    let char = longString[index]\n    // Hacer algo con char\n    index = longString.index(after: index) // ¡O(1)!\n}
\n\n

Ahora la complejidad es lineal O(n). Pero, ¿qué pasa si necesitamos acceso aleatorio por índice, no secuencial?

\n\n

Optimización n.º 2: Conversión a una matriz de caracteres (con salvedades)

\n\n

Si necesita acceder con frecuencia a los caracteres por un índice arbitrario (enésimo carácter), y la cadena no es gigantesca, lo más sencillo es convertirla en [Character]. Entonces el acceso será O(1).

\n\n
let str = "¡Hola, Swift!"\nlet chars = Array(str)\nlet char = chars[2] // "o                

Blogs

Book Recommendations