Iterações complexas em Haskell
-
06-07-2019 - |
Pergunta
Eu tenho esse complexo programa de iterações que escrevi no TI Basic para realizar uma iteração básica em um número complexo e depois dar a magnitude do resultado:
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
O que eu gostaria de fazer é fazer uma versão Haskell disso para impressionar meu professor em uma tarefa. Ainda estou apenas aprendendo e cheguei até aqui:
fractal ::(RealFloat a) =>
(Complex a) -> (Integer a) -> [Complex a]
fractal c n | n == a = z : fractal (z^2 + c)
| otherwise = error "Finished"
O que eu não sei como fazer é como fazê -lo apenas iterado n
vezes, então eu queria ter isso contando a
e então compare com n
Para ver se havia terminado.
Como eu iria fazer isso?
Solução
A resposta de NewAcct mostra o caminho:
fractal c n = take n $ iterate (\z -> z^2 + c) c
Iterate
Gera a lista infinita de aplicativos repetidos. Ex:
iterate (2*) 1 == [1, 2, 4, 8, 16, 32, ...]
Em relação à IO, você terá que fazer alguns cálculos monádicos.
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)
Como o complexo é conversível de e para strings por padrão, você pode usar readLn
para lê -los no console (o formato é Re :+ Im
).
EDIT: Apenas por diversão, alguém poderia desugar a sintaxe monádica e as assinaturas de tipo que comprimiriam todo o programa para isso:
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)
Edit #2: Alguns links relacionados à plotagem e conjuntos de Mandelbrot.
Outras dicas
Bem, você sempre pode gerar uma lista infinita de resultados de aplicativos repetidos e tomar o primeiro n
deles usando take
. E a iterate
A função é útil para gerar uma lista infinita de resultados de aplicativos repetidos.
Se você quiser uma lista de valores:
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 você se preocupa apenas com o último resultado:
fractal c n = fractalHelper c c n
where
fractalHelper z c 0 = z
fractalHelper z c n = fractalHelper (z^2 + c) c (n-1)
Basicamente, em ambos os casos, você precisa de uma função auxiliar para a contagem e acumulação. Agora tenho certeza de que há uma maneira melhor/menos detalhada de fazer isso, mas eu sou um novato em Haskell.
Edit: Just for Kicks, uma liner-liner:
fractalFold c n = foldr (\c z -> z^2 + c) c (take n (repeat c))
(Embora a coisa (Take n (repetir c)) pareça meio desnecessária, deve haver uma maneira ainda melhor)