2009-09-22 7 views
17

Hoy estaba experimentando con bloques de Objective-C, así que pensé que sería inteligente y añadir a NSArray unos métodos de recolección de estilo funcional que he visto en otros idiomas:El uso de Objective-C Bloques

@interface NSArray (FunWithBlocks) 
- (NSArray *)collect:(id (^)(id obj))block; 
- (NSArray *)select:(BOOL (^)(id obj))block; 
- (NSArray *)flattenedArray; 
@end 

El método collect: toma un bloque que se llama para cada elemento de la matriz y se espera que devuelva los resultados de alguna operación utilizando ese elemento. El resultado es la recopilación de todos esos resultados. (Si el bloque devuelve nulo, no se agrega nada al conjunto de resultados.)

El método select: devolverá una nueva matriz con solo los elementos del original que, cuando se pasa como argumento al bloque, el bloque devuelto SÍ.

Y, por último, el método flattenedArray itera sobre los elementos de la matriz. Si un elemento es una matriz, recursivamente llama a flattenedArray y agrega los resultados al conjunto de resultados. Si el elemento no es una matriz, agrega el elemento al conjunto de resultados. El conjunto de resultados se devuelve cuando todo está terminado.

Así que ahora que tenía algo de infraestructura, necesitaba un caso de prueba. Decidí encontrar todos los archivos del paquete en los directorios de la aplicación del sistema. Esto es lo que se me ocurrió:

NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) collect:^(id path) { return (id)[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil] collect:^(id file) { return (id)[path stringByAppendingPathComponent:file]; }]; }] flattenedArray] select:^(id fullPath) { return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; }]; 

Sí, eso es una línea y es horrible. Probé algunos enfoques para agregar nuevas líneas y sangrías para tratar de limpiarlo, pero todavía parece que el algoritmo real se pierde en todo el ruido. Sin embargo, no sé si solo se trata de una cuestión de sintaxis o de mi relativa experiencia en relación con el uso de un estilo funcional.

Para la comparación, me decidieron a hacerlo "de la manera antigua" y sólo tiene que utilizar bucles:

NSMutableArray *packagePaths = [NSMutableArray new]; 
for (NSString *searchPath in NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES)) { 
    for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:searchPath error:nil]) { 
     NSString *packagePath = [searchPath stringByAppendingPathComponent:file]; 
     if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:packagePath]) { 
      [packagePaths addObject:packagePath]; 
     } 
    } 
} 

OMI esta versión fue más fácil de escribir y es más fácil de leer para arrancar.

Supongo que es posible que este sea de alguna manera un mal ejemplo, pero parece una forma legítima de usar bloques para mí. (¿Estoy equivocado?) ¿Me falta algo sobre cómo escribir o estructurar el código Objective-C con bloques que limpiarían esto y lo dejarían más claro que (o incluso tan claro como) la versión en bucle?

Respuesta

19

Use nuevas líneas y desglose su llamada en múltiples líneas.

El patrón estándar utilizado en todas las API de Apple es que un método o función solo debe tomar un argumento de bloque y ese argumento siempre debe ser el último.

Lo que has hecho. Bueno.

Ahora, al escribir el código que utiliza dicho API, hacer algo como:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES); 
paths = [paths collect: ^(id path) { 
    ... 
}]; 
paths = [paths collect: ^(id path) { 
    ... 
}]; 
paths = [paths select: ^(id path) { 
    ... 
}]; 

es decir, haga cada paso de su colección/seleccionar/filtrar/aplanar/mapear/lo que sea como un paso separado. Esto no será más rápido/más lento que las llamadas a métodos encadenados.

Si necesita bloques de nido de lado de los bloques, y luego hacerlo con plena sangría:

paths = [paths collect: ^(id path) { 
    ... 
    [someArray select:^(id path) { 
     ... 
    }]; 
}]; 

Al igual if anidadas o similares. Cuando se vuelve demasiado complejo, refactorícelo en funciones o métodos, según sea necesario.

+1

buena solución, aunque no soy un gran fan de la redefinición de 'paths' varias veces. Probablemente terminaré nombrando varios valores basados ​​en su contenido, y terminaré con uno llamado 'paths'. Solo mis dos centavos. –

+0

La animación de UIView usa 2 bloques. –

+0

Dijo patrón no canon. Esos métodos que toman todos los bloques tienen todos los bloques como los parámetros finales. Además, esas API aparecieron en iOS 4, que fue mucho después de esta publicación. – logancautrell

2

Creo que el problema es que (al contrario de lo que afirman los críticos de Python) el espacio en blanco es importante. En un estilo más funcional, parece que tendría sentido copiar el estilo de otros lenguajes funcionales. La forma más LISP-y escribir su ejemplo podría ser algo como:

NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) 
          collect:^(id path) { 
             return [[[NSFileManager defaultManager] 
               contentsOfDirectoryAtPath:path 
                    error:nil] 
               collect:^(id file) { 
                 return [path stringByAppendingPathComponent:file]; 
                 } 
              ]; 
            } 
          ] 
          flattenedArray 
          ] 

          select:^(id fullPath) { 
            return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; 
           } 
         ]; 

Yo no diría que este es más clara que la versión en bucle. Como cualquier otra herramienta, los bloques son una herramienta y deben usarse solo cuando son la herramienta adecuada para el trabajo. Si la legibilidad sufre, diría que no es la mejor herramienta para el trabajo. Los bloques son, después de todo, una adición a un lenguaje fundamentalmente imperativo. Si realmente desea la concisión de un lenguaje funcional, use un lenguaje funcional.

Cuestiones relacionadas