El único problema de los threads es que hay que evitar a toda costa las condiciones de carrera.
Volviendo al tema de la recursividad, ayer BOFH llegó por Twitter a la conclusión de que el factorial tenía una versión iterativa, es decir, que se puede hacer con while. Y tiene toda la razón. Todo algoritmo recursivo tiene su versión iterativa. En cada caso, una puede ser más eficiente que la otra; incluso la eficiencia puede cambiar si modificamos el tamaño de los valores de entrada.
Ahora os voy a mostrar un ejemplo práctico de recursividad, para que veáis que no todo son matemáticas. Se trata de un generador de índices html. Por cada carpeta se enumeran sus subcarpetas y archivos, y se genera un index.html en cada carpeta con todo. ¡Vamos a ello!
El programa empieza aquí:
Sub Main()
generar App.Path, False
MsgBox ("Los archivos html se han generado correctamente")
End 'salimos del programa
End Sub
Sub generar(ruta As String, esSubRuta As Boolean)
Dim ts As TextStream, fldr As Folder, fil As File, fso As New FileSystemObject 'definición de variables
Set fldr = fso.GetFolder(ruta)
Set ts = fso.CreateTextFile(ruta & "\index.html")
ts.WriteLine ("<html><head>")
ts.WriteLine ("<title>Carpeta " & fldr.Name & "</title></head><body>")
ts.WriteLine ("<h1>Archivos y carpetas en el directorio " & fldr.Name & "</h1>")
ts.WriteLine ("<table><tr><th>Nombre</th><th>Fecha de modificación</th><th>Tamaño</th></tr>")
If esSubRuta = True Then 'ponemos un enlace para volver al directorio de nivel superior
ts.WriteLine ("<tr><td><a href=" & Chr(34) & "../index.html" & Chr(34) & ">Ir al directorio de nivel superior</a></td></tr>")
End If
Dim subruta As Folder, carpetas As Folders, archivos As Files
Set carpetas = fldr.SubFolders
For Each subruta In carpetas
ts.WriteLine ("<tr><td><a href=" & Chr(34) & subruta.Name & "/index.html" & Chr(34) & ">" & subruta.Name & "/</a></td>")
ts.WriteLine ("<td>" & subruta.DateLastModified & "</td>")
Select Case subruta.Size 'vamos a mostrar los tamaños de la mejor manera posible
Case 0 To 1000
ts.WriteLine ("<td>" & Round(subruta.Size, 2) & " B</td></tr>")
Case 1001 To 1000000
ts.WriteLine ("<td>" & Round(subruta.Size / 1024, 2) & " KB</td></tr>")
Case 1000001 To 1000000000
ts.WriteLine ("<td>" & Round(subruta.Size / 1024 / 1024, 2) & " MB</td></tr>")
Case 1000000001 To 1000000000000#
ts.WriteLine ("<td>" & Round(subruta.Size / 1024 / 1024 / 1024, 2) & " GB</td></tr>")
End Select
generar subruta.Path, True
Next 'se acaba el bucle for
Set archivos = fldr.Files
For Each fil In archivos
If Not (fil.Name = "index.html" Or fil.Name = App.EXEName & ".exe") Then 'listamos todos excepto index.html y este ejecutable
ts.WriteLine ("<tr><td><a href=" & Chr(34) & fil.Name & Chr(34) & ">" & fil.Name & "</a></td>")
ts.WriteLine ("<td>" & fil.DateLastModified & "</td>")
Select Case fil.Size
Case 0 To 1000
ts.WriteLine ("<td>" & Round(fil.Size, 2) & " B</td></tr>")
Case 1001 To 1000000
ts.WriteLine ("<td>" & Round(fil.Size / 1024, 2) & " KB</td></tr>")
Case 1000001 To 1000000000
ts.WriteLine ("<td>" & Round(fil.Size / 1024 / 1024, 2) & " MB</td></tr>")
Case 1000000001 To 1000000000000#
ts.WriteLine ("<td>" & Round(fil.Size / 1024 / 1024 / 1024, 2) & " GB</td></tr>")
End Select
End If
Next
ts.WriteLine ("</table>")
ts.WriteLine ("</body></html>")
ts.Close
End Sub
El programa acaba aquí.
Está hecho en Visual Basic 6. He puesto algún que otro comentario porque entiendo que algunas instrucciones pueden ser difíciles de interpretar si no conocéis el lenguaje.
Y bien, ¿para qué sirve? Yo en su momento lo desarrollé para que la carpeta public de Dropbox fuese explorable desde fuera. Se daban las condiciones idóneas: los enlaces públicos guardaban relación con la estructura de la carpeta en el disco, pero Dropbox no ofrecía un índice. Vino como anillo al dedo, como una llave que encaja en la cerradura. Si todavía usáis esa carpeta y queréis el ejecutable compilado, decídmelo y os lo subo a algún sitio. Volviendo al tema que nos ocupa, ¿a que sería jodido hacer una versión iterativa de esto?