2010-11-21 12 views
6

se puede convertirforma libre puntos frente estilo

-- tupleUnfold :: forall a. ((forall b. a -> b)) -> a -> ((b)) 
tupleUnfold :: Int -> ExpQ 
tupleUnfold n = do 
    xs <- forM [1 .. n] (const . newName $ "x") 
    y <- newName "y" 
    let y' = varE y 
     g (ps', es') x = (varP x : ps', appE (varE x) y' : es') 
     (ps, es) = foldl' g ([], []) xs 
    lamE [tupP ps, varP y] (tupE es) 

a pointfree estilo a la vez que mantiene la claridad (sé de la 'pointfree' programa, pero preferiría no ofuscar el código aún más)?

De cualquier manera, ¿qué cambios se pueden hacer para mejorar el estilo de la función, o de lo contrario hace que su intención sea más clara? La función está destinada a ser utilizada de la siguiente manera.

$(tupleUnfold 3) ((+ 1), (+ 2), (+ 3)) 2 
-- (3, 4, 5) 

¿Cuáles son algunas de las convenciones de nomenclatura para utilizar mejor (véase el PS, PS', ES, y es' variables)?

Respuesta

5

Esto es lo que obtuve. Necesita Control.Arrow (&&&) y Control.Applicative (<$>).

No se pudo sacar ventaja en ello mucho más sin que sea totalmente incomprensible.

EDITAR Aunque no apunta libre, aquí es la más clara que podría hacerlo. Necesidades Data.Function (on)

tupleUnfold :: Int -> ExpQ 
tupleUnfold n = do 
    y <- newName "y" 
    xs <- replicateM n (newName "x") 
    let exps = tupE $ zipWith appVars xs (repeat y) 
     pats = tupP $ map varP xs 
    lamE [pats, varP y] exps 
    where 
    appVars = appE `on` varE 
+0

Me gusta el uso de "replicateM" - no estoy seguro de cómo me olvidé de ese – ScootyPuff

+0

Estoy marcando este como la respuesta correcta de forma un tanto arbitraria. Aunque el otro es sin puntos en el nivel superior, este parece representar mejor el flujo y la transformación, aunque es un poco más difícil de leer. – ScootyPuff

+0

Parte de la razón para el doblez (en lugar del mapa) era evitar múltiples recorridos. También sé que GHC es bastante bueno en la fusión de listas cruzadas. ¿Es usualmente una diferencia significativa? Dado que ambas formas son relativamente fáciles de escribir, ¿cuál debería (como regla general) preferir? – ScootyPuff

0

personalmente creo que está bastante claro ya, pero ¿qué tal esto:

tupleUnfold :: Int -> ExpQ 
tupleUnfold = mapM (const . newName $ "x") . enumFromTo 1 >=> \xs -> do 
    y <- newName "y" 
    let y' = varE y 
     g (ps', es') x = (varP x : ps', appE (varE x) y' : es') 
     f ps = lamE [tupP ps, varP y] . tupE 
    uncurry f $ foldl' g ([],[]) xs 

El operador Kleisli composición >=> (de Control.Monad) es útil para crear funciones monádicos pointfree.

+0

que tenía una versión que estaba en el pointfree de nivel superior (usando> =>), pero finalmente tuvo que descomponerlo. Es bueno saber que está relativamente claro tal como es. La mayoría de lo que me atrapa de Haskell es de cuántas maneras se podría reescribir algo. – ScootyPuff

+0

¿Qué tan importantes son las encuadernaciones en lo que respecta al rendimiento? Esa es la razón principal por la que tengo y '- No estaba seguro de si la expresión llegaría a un ámbito externo. – ScootyPuff

+0

Sí sin el y 'el cómputo 'varE y' no sería compartido. Pero ese cálculo probablemente solo delegue en un constructor, por lo que es irrelevante (y no te preocupes por dos llamadas a un constructor vs. uno, eso es solo dejar que tu lenguaje sea tuyo, no obtendrás un código más rápido, tú " llévate un código horrible). El primo en foldl 'es inútil porque es una tupla normal (floja). Utilice una tupla estricta 'datos Tuple a b = Tuple! A! B'. Pero como se trata de un código tipo secuencia, use 'foldr' en su lugar, utiliza menos memoria de manera asintótica. Esto básicamente equivale a 'descomprimir. mapa' como en mi respuesta. – luqui

1

un poco más incomprensible (tratar de leer de derecha a izquierda):

tupleUnfold n = do 
    y <- newName "y" 
    uncurry lamE . ((:[varP y]) . tupP *** tupE) . unzip . 
    map (varP &&& (`appE` varE y) . varE) <$> replicateM n (newName "x") 

EDITAR:
mezcla de flechas y la composición de funciones para el procesamiento

tupleUnfold n = do 
    y <- newName "y" 
    uncurry lamE . ((tupP >>> (:[varP y])) *** tupE) . unzip . 
    map (varP &&& (varE >>> (`appE` varE y))) <$> replicateM n (newName "x") 

y usando principalmente flechas (leer la función de procesamiento de izquierda a derecha t)

tupleUnfold n = do 
    y <- newName "y" 
    (map (varP &&& (varE >>> (`appE` varE y))) >>> unzip >>> 
    ((tupP >>> (:[varP y])) *** tupE) >>> uncurry lamE) <$> replicateM n (newName "x") 

nota que la función de flecha (>>>) es equivalente a flip (.)

Cuestiones relacionadas