2010-05-24 11 views
9

Supongamos que tengo una función con la firma siguiente tipo:Utilización de los elementos en una lista como argumentos

g :: a -> a -> a -> b 

que también tienen una lista de a s-llamémoslo xs -que sé que van a contener al menos tres artículos. Me gustaría aplicar g a los primeros tres elementos de xs. Sé que podría definir un combinador como la siguiente:

($$$) :: (a -> a -> a -> b) -> [a] -> b 
f $$$ (x:y:z:_) = f x y z 

Entonces podría simplemente utilizar g $$$ xs. Esto hace que $$$ sea un poco como uncurry, pero para una función con tres argumentos del mismo tipo y una lista en lugar de una tupla.

¿Hay alguna manera de hacer esto idiomáticamente con los combinadores estándar? O mejor dicho, ¿cuál es la forma más idiomática de hacer esto en Haskell? Pensé que intentar pointfree en una versión no infija de $$$ podría darme una idea de por dónde empezar, pero la salida era una abominación con 10 flip s, un puñado de head sy tail sy ap s, y 28 paréntesis.

(NB: Sé que esto no es terriblemente una cosa de Haskelly para hacer, en primer lugar, pero he encontrado un par de situaciones en las que parece una solución razonable, especialmente cuando se usa Parsec. ciertamente aceptar "no siempre hacer esto en código real" si esa es la mejor respuesta, pero yo prefiero ver algunos truco inteligente que implica la mónada ((->) r) o lo que sea.)

+4

No veo cuál es el problema con el código que tiene. Es corto y al grano. No todo tiene que ser (o debería ser) sin punto. – sepp2k

+1

No creo necesariamente que haya nada de malo en ello: solo tengo curiosidad acerca de si hay una forma concisa de hacer esto sin definir un nuevo combinador como '$$$'. –

+0

Tenga en cuenta que esta es una función parcial, por lo que ... probablemente sea una mala idea en general :) Puede que sea mejor con un '($$$) :: (a -> a -> a -> b) -> [a] -> Tal vez b' –

Respuesta

12

O más bien, ¿cuál es la forma más idiomática para hacer esto en Haskell?

Idiomatic? Si realmente quieres una función que hace lo que hace ($$$), el código que tienes es probablemente tan idiomático como obtendrás.

preferiría ver algunos truco inteligente

Oh, bueno, en la que caso.

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
class ListApply f a r | f -> a r where 
    ($...) :: f -> [a] -> r 

instance (TypeCast b r) => ListApply b a r where 
    x $... _ = typeCast x 

instance (ListApply f a r) => ListApply (a -> f) a r where 
    f $... (x:xs) = (f x) $... xs 

hay que ir, una solución completamente generales: Dada una función de aridad arbitraria con una firma como a -> a ... -> b, aplicarlo a la mayor cantidad de elementos de una lista [a] según sea necesario.Una demostración:

ones :: [Int] 
ones = repeat 1 

test1 x = x 
test2 x y = x + y 
test3 x y z = (x + z) * (y + z) 

En GHCi:

> test1 $... ones 
1 
> test2 $... ones 
2 
> test3 $... ones 
4 

duda va a aceptar "no vuelvas a hacer esto en código real" si esa es la mejor respuesta

Usted Probablemente quiera ir con eso.


Ah, y un poco de texto modelo necesario para ejecutar el código anterior:

class TypeCast a b | a -> b, b->a where typeCast :: a -> b 
class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t->a->b 
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b 
instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast'() x 
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast'' 
instance TypeCast''() a a where typeCast'' _ x = x 

Ésta es la navaja suiza de metaprogramming de nivel tipo, cortesía de Oleg Kiselyov.

7
f $$$ (x:y:z:_) = f x y z 

en mi opinión esta es la forma más idiomática y concisa. Si el número de argumentos es variable, puede utilizar la plantilla de Haskell o hacerlo de forma iterativa - Definir:

zero = const 
next n f (x:xs) = n (f x) xs 

entonces su función es next (next (next zero))), y esto funciona para cualquier anidamiento de next.

También se puede romper a combinadores más primitivas:

firstThree (x:y:z:_) = (x,y,z) 
uncurry3 f (x,y,z) = f x y z 
g f = uncurry3 f . firstThree 
Cuestiones relacionadas