La mia applicazione moltiplica i vettori dopo una (costosa) conversione utilizzando una FFT. Di conseguenza, quando scrivoMoltiplicazione memorizzabile
f :: (Num a) => a -> [a] -> [a]
f c xs = map (c*) xs
solo voglio calcolare la FFT di c
una volta, piuttosto che per ogni elemento di xs
. Non c'è davvero alcuna necessità di memorizzare la FFT di c
per l'intero programma, solo nell'ambito locale.
ho tentato di definire la mia Num
istanza come:
data Foo = Scalar c
| Vec Bool v -- the bool indicates which domain v is in
instance Num Foo where
(*) (Scalar c) = \x -> case x of
Scalar d -> Scalar (c*d)
Vec b v-> Vec b $ map (c*) v
(*) v1 = let Vec True v = fft v1
in \x -> case x of
Scalar d -> Vec True $ map (c*) v
v2 -> Vec True $ zipWith (*) v (fft v2)
Poi, in una domanda, che io chiamo una funzione simile a f
(che funziona su arbitrarie Num
s) dove c=Vec False v
, e mi aspettavo che questo sarebbe essere altrettanto veloce come se io hackero f
a:
g :: Foo -> [Foo] -> [Foo]
g c xs = let c' = fft c
in map (c'*) xs
la funzione g
rende il Memoizzazione di fft c
verifica, ed è mu ch più veloce di chiamare f
(non importa come definisco (*)
). Non capisco cosa c'è che non va con f
. È la mia definizione di nell'istanza Num
? Ha qualcosa a che fare con f
che funziona su tutti i Num, e GHC quindi non è in grado di capire come calcolare parzialmente (*)
?
Nota: Ho controllato l'output principale per la mia istanza Num e (*) è effettivamente rappresentato come lambda nidificato con la conversione FFT nel lambda di livello superiore. Quindi sembra che questo sia almeno in grado di essere memoized. Ho anche provato sia l'uso giudizioso e sconsiderato di modelli di scoppio per tentare di forzare la valutazione senza alcun effetto.
Come nota a margine, anche se io riesco a capire come fare (*)
Memoize suo primo argomento, c'è ancora un altro problema di come si è definito: Un programmatore voler uso il tipo di dati Foo deve sapere su questa capacità di memorizzazione. Se ha scritto
map (*c) xs
non si verificherebbe alcuna memorizzazione. (Deve essere scritto come (map (c*) xs))
Ora che ci penso, non sono del tutto sicuro di come sarebbe GHC riscrivere la versione (*c)
da quando sono al curry (*)
Ma ho fatto un test rapido per verificare che sia (*c)
e (c*)
funzionare come previsto.: il primo argomento a *
, mentre (*c)
fa c
il secondo argomento a *
. Quindi il problema è che non è ovvio come si dovrebbe scrivere la moltiplicazione per garantire la memoizzazione.È questo solo un lato negativo intrinseco alla notazione infisso (e implicita che gli argomenti *
sono simmetrici)?
il secondo problema meno pressante è che il caso whe mappiamo (v *) su un elenco di scalari. In questo caso, (si spera) il fft di v sarebbe calcolato e memorizzato, anche se non è necessario poiché l'altro multiplo è uno scalare. C'è un modo per aggirare questo?
Grazie
compilazione con '-O2' e benchmarking con codice compilato? – jberryman
Sì, ma ho controllato il core, e non sembra che stia compilando nulla. – crockeea
BTW, 'let c '= fft c nella mappa (c *) xs' fa * not * calcola un' fft', perché Haskell è pigro. "Non c'è mai usato, quindi non verrà mai calcolato. – luqui