Iterazioni complesse in haskell
-
06-07-2019 - |
Domanda
Ho questo complesso programma di iterazioni che ho scritto in TI Basic per eseguire un'iterazione di base su un numero complesso e quindi dare la grandezza del risultato:
INPUT “SEED?”, C
INPUT “ITERATIONS?”, N
C→Z
For (I,1,N)
Z^2 + C → Z
DISP Z
DISP “MAGNITUDE”, sqrt ((real(Z)^2 + imag(Z)^2))
PAUSE
END
Quello che vorrei fare è fare una versione di Haskell per stupire il mio insegnante in un compito. Sto ancora imparando e sono arrivato così lontano:
fractal ::(RealFloat a) =>
(Complex a) -> (Integer a) -> [Complex a]
fractal c n | n == a = z : fractal (z^2 + c)
| otherwise = error "Finished"
Quello che non so come fare è come farlo iterare solo n
volte, quindi volevo che contasse a
e poi confrontarlo con n
per vedere se era finito.
Come potrei farlo?
Soluzione
La risposta di Newacct mostra il modo:
fractal c n = take n $ iterate (\z -> z^2 + c) c
Iterate
genera l'elenco infinito di applicazioni ripetute.
Es:
iterate (2*) 1 == [1, 2, 4, 8, 16, 32, ...]
Per quanto riguarda l'IO, dovrai fare alcuni calcoli monadici.
import Data.Complex
import Control.Monad
fractal c n = take n $ iterate (\z -> z^2 + c) c
main :: IO ()
main = do
-- Print and read (you could even omit the type signatures here)
putStr "Seed: "
c <- readLn :: IO (Complex Double)
putStr "Number of iterations: "
n <- readLn :: IO Int
-- Working with each element the result list
forM_ (fractal c n) $ \current -> do
putStrLn $ show current
putStrLn $ "Magnitude: " ++ (show $ magnitude current)
Poiché Complex è convertibile da e in stringhe per impostazione predefinita, è possibile utilizzare readLn
per leggerli dalla console (il formato è Re: + Im
).
Modifica: solo per divertimento, si potrebbe desugar la sintassi monadica e digitare le firme che comprimerebbero l'intero programma a questo:
main =
(putStr "Seed: ") >> readLn >>= \c ->
(putStr "Number of iterations: ") >> readLn >>= \n ->
forM_ (take n $ iterate (\z -> z^2 + c) c) $ \current ->
putStrLn $ show current ++ "\nMagnitude: " ++ (show $ magnitude current)
Modifica n. 2: alcuni link relativi alla trama e ai set di Mandelbrot.
Altri suggerimenti
Beh, puoi sempre generare un elenco infinito di risultati di applicazioni ripetute e prenderne il primo n
usando take
. E la funzione iterate
è utile per generare un elenco infinito di risultati di applicazioni ripetute.
Se desideri un elenco di valori:
fractalList c n = fractalListHelper c c n
where
fractalListHelper z c 0 = []
fractalListHelper z c n = z : fractalListHelper (z^2 + c) c (n-1)
Se ti interessa solo l'ultimo risultato:
fractal c n = fractalHelper c c n
where
fractalHelper z c 0 = z
fractalHelper z c n = fractalHelper (z^2 + c) c (n-1)
Fondamentalmente, in entrambi i casi è necessaria una funzione di supporto per il conteggio e l'accumulo. Ora sono sicuro che ci sia un modo migliore / meno prolisso per farlo, ma sono praticamente un neofita di Haskell da solo.
Modifica: solo per i calci, un foldr one-liner:
fractalFold c n = foldr (\c z -> z^2 + c) c (take n (repeat c))
(anche se la cosa (take n (repeat c)) sembra un po 'superflua, deve esserci un modo ancora migliore)