Realmente no puedo responder el # 2 sin perderme (hay muchas dimensiones a lo largo de las cuales puedes comparar estas estructuras), pero para el # 3 la respuesta es bastante simple.
Use una estructura de datos imperativa si: (a) no hay absolutamente ningún aliasing, o (b) realmente necesita usar aliasing para una transmisión eficiente.
Si no hay alias de su estructura de datos, entonces no está aprovechando el hecho de que las estructuras de datos funcionales son persistentes. Por lo tanto, no hay razón para pagar su costo. Hay dos advertencias a este consejo. Primero, puede preferir la simplicidad de la implementación de una estructura de datos funcional: implementar la eliminación de un árbol rojo-negro funcional lo hará maldecir, pero implementar la eliminación en un árbol rojo-negro imperativo con punteros principales lo dejará contemplando el suicidio. En segundo lugar, la asignación puede ser más costosa de lo que espera en un lenguaje gc'd, ya que las escrituras pueden sacar las estructuras de datos de la generación joven. Realmente no tenemos una buena teoría de los efectos de caché y gc, por lo que no tiene más remedio que hacer una evaluación comparativa.
En segundo lugar, si necesita un canal de transmisión, una estructura de datos compartidos es una excelente manera de hacerlo. Con una actualización de tiempo constante, puede decir arbitrariamente a muchas otras personas que un valor ha cambiado. (Esta es la razón por la cual union-find es una estructura de datos tan excelente). Con una configuración puramente funcional, necesita modificar a todas esas otras personas o darles punteros abstractos en un estado que codifica manualmente (que es una especie de obtuso cosas que hacer).
Si no desea razonar sobre el alias y la propiedad del objeto, o si necesita varias versiones de la misma estructura de datos (necesita una versión nueva y una antigua, por ejemplo), simplemente use una estructura de datos funcional.
El lugar donde encuentro que sigue estos consejos lo más difícil es con algoritmos gráficos. Hay muchos algoritmos de gráficos imperativos realmente elegantes, pero a menudo es el caso (por ejemplo, al escribir compiladores) que también desea persistencia. Por lo general, las personas intentan dividir la diferencia y usan el algoritmo imperativo genial, pero intentan atornillar las versiones a un lado para obtener persistencia. Esto generalmente es bastante horrible, está lleno de errores y es propenso a perder la ventaja de rendimiento del algoritmo imperativo.