2010-05-24 4 views
9

Supposons que j'ai une fonction avec la signature de type:Utilisation des éléments dans une liste comme arguments

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

J'ai aussi une liste des a s-appelons-le xs -Que je sais que contiendra au moins trois articles. Je voudrais appliquer g aux trois premiers articles de xs. Je sais que je pourrais définir un combinateur comme ce qui suit:

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

Je pouvais utiliser g $$$ xs. Cela fait $$$ un peu comme uncurry, mais pour une fonction avec trois arguments du même type et une liste au lieu d'un tuple. Y a-t-il un moyen de faire cela de façon idiomatique en utilisant des combinateurs standards? Ou plutôt, quelle est la manière la plus idiomatique de le faire dans Haskell? Je pensais essayer pointfree sur une version non-infixe de $$$ pourrait me donner une idée de par où commencer, mais la sortie était une abomination avec 10 flip s, une poignée de head s et tail s et ap s, et 28 parenthèses. (NB: Je sais que ce n'est pas une chose terriblement Haskelly à faire en premier lieu, mais j'ai rencontré quelques situations où cela semble être une solution raisonnable, surtout en utilisant Parsec. certainement accepter « ne pas jamais faire dans votre code » si c'est la meilleure réponse, mais je préfère voir astuce impliquant la ((->) r) monade ou autre.)

+4

Je ne vois pas quel est le problème avec le code que vous avez. C'est court et précis. Tout ne doit pas (ou devrait être) sans point. – sepp2k

+1

Je ne pense pas nécessairement qu'il y ait quelque chose qui ne va pas - je suis simplement curieux de savoir s'il existe une façon concise de le faire sans définir un nouveau combinateur comme «$$$». –

+0

Notez qu'il s'agit d'une fonction partielle, donc ... probablement une mauvaise idée en général :) Vous pourriez être mieux avec un '($$$) :: (a -> a -> a -> b) -> [a] -> Peut-être b' –

Répondre

12

Ou plutôt, quelle est la façon la plus idiomatiques de le faire dans Haskell?

Idiomatique? Si vous voulez vraiment une fonction qui fait ce que fait ($$$), le code que vous avez est probablement aussi idiomatique que vous aurez.

Je préfère voir astuce

Oh, eh bien, dans que cas.

{-# 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 

Là vous allez, une solution entièrement générale: Etant donné une fonction d'arité arbitraire avec une signature comme a -> a ... -> b, l'appliquer à autant d'éléments d'une liste [a] si nécessaire.Une démonstration:

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 

Je vais certainement accepter "ne jamais faire dans votre code" si c'est la meilleure réponse

Vous probablement vouloir aller avec ça.


Oh, et un peu de boilerplate nécessaire pour exécuter le code ci-dessus:

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 

C'est le couteau suisse de metaprogramming niveau de type, avec la permission de Oleg Kiselyov.

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

à mon avis, ce est la manière la plus idiomatique et la plus concise. Si le nombre d'arguments est variable, vous pouvez utiliser le modèle Haskell ou faire itérativement - définir:

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

alors votre fonction est next (next (next zero))), et cela fonctionne pour toute imbrication de next.

vous pouvez également le casser à combinators plus primitifs:

firstThree (x:y:z:_) = (x,y,z) 
uncurry3 f (x,y,z) = f x y z 
g f = uncurry3 f . firstThree 
Questions connexes