2010-05-24 16 views
9

Supponiamo che io ho una funzione con la seguente firma tipo:Uso delle voci in una lista come argomenti

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

Ho anche una lista di a s-chiamiamolo xs -che so che conterrà almeno tre elementi. Vorrei applicare g ai primi tre articoli di xs. So che potrei definire un combinatore come la seguente:

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

Poi ho potuto solo usare g $$$ xs. Questo rende $$$ un po 'come uncurry, ma per una funzione con tre argomenti dello stesso tipo e una lista invece di una tupla.

C'è un modo per farlo in modo idiomatico utilizzando i combinatori standard? O meglio, qual è il modo più idiomatico per farlo in Haskell? Ho pensato di provare pointfree su una versione non infissa di $$$ potrebbe darmi un'idea di dove iniziare, ma l'output era un abominio con 10 flip s, una manciata di head se tail se ap s e 28 parentesi.

(NB: So che questa non è una cosa terribilmente Haskelly da fare in primo luogo, ma mi sono imbattuto in un paio di situazioni in cui sembra una soluzione ragionevole, specialmente quando si usa Parsec. certamente accettare "non mai fare questo nel codice vero e proprio" se questa è la risposta migliore, ma preferirei vedere qualche trucco intelligente che coinvolge la monade ((->) r) o qualsiasi altra cosa.)

+4

Non vedo cosa c'è di sbagliato nel codice che hai. È breve e al punto. Non tutto deve essere (o dovrebbe essere) senza punti. – sepp2k

+1

Non penso necessariamente che ci sia qualcosa di sbagliato in questo: sono solo curioso di sapere se c'è un modo conciso per farlo senza definire un nuovo combinatore come "$$$". –

+0

Nota che questa è una funzione parziale, quindi ... probabilmente una cattiva idea in generale :) Potresti star meglio con un '($$$) :: (a -> a -> a -> b) -> [a] -> Forse b' –

risposta

12

O meglio, qual è il modo più idiomatico per fare questo in Haskell?

Idiomatico? Se vuoi davvero una funzione che fa ciò che fa ($$$), il codice che hai è probabilmente il più idiomatico di quello che otterrai.

Preferirei vedere qualche trucco intelligente

Oh, beh, in quel 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 

ci si va, una soluzione completamente generale: Data una funzione di arità arbitraria con una firma come a -> a ... -> b, applicarlo ad altrettanti elementi di una lista [a] se necessario.Una dimostrazione:

ones :: [Int] 
ones = repeat 1 

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

In GHCi:

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

sarò certamente Accetto "Non bisogna mai fare questo nel codice vero e proprio" se questa è la risposta migliore

È probabilmente voglio andare con quello.


Oh, e un po 'di testo standard necessari per eseguire il codice sopra:

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 

Questo è il coltellino svizzero di tipo a livello metaprogrammazione, per gentile concessione di Oleg Kiselyov.

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

a mio parere questo è il modo più idiomatico e conciso. Se il numero di argomenti è variabile, è possibile utilizzare Template Haskell o farlo in modo iterativo - definire:

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

quindi la funzione è next (next (next zero))), e questo funziona per qualsiasi nidificazione di next.

Inoltre è possibile rompere a combinatori più primitive:

firstThree (x:y:z:_) = (x,y,z) 
uncurry3 f (x,y,z) = f x y z 
g f = uncurry3 f . firstThree 
Problemi correlati