Я полностью документирую каждый открытый метод в каждом классе API. Классы, которые имеют общедоступных участников, но которые не предназначаются для внешнего потребления, заметно отмечены в классе javadoc. Я также документирую каждый защищенный метод в каждом классе API, хотя до меньшей степени. Это идет на идею, что у любого разработчика, который расширяет класс API, уже будет справедливое понятие того, что продолжается.
Наконец, я буду иногда документировать частный и закрытые методы пакета для моего собственного преимущества. Для любого метода или поля, что я думаю, нужно некоторое объяснение в его использовании, получит документацию, независимо от его видимости.
yairchu имеет хороший ответ для вашего проблема с printBody. Ваш главный вопрос, как структурировать вашу программу, чтобы вы могли распечатать каждый шаг, немного сложнее. Предположительно, вы хотите сохранить runSim
или что-то в этом роде в чистом виде, поскольку он просто запускает симуляцию, а ввод-вывод на самом деле не его работа.
Я бы подошел к этому двумя способами: либо заставьте runSim возвращать бесконечный список шагов моделирования, либо заставьте оболочку ввода-вывода запускать только один шаг за раз. Я предпочитаю первый вариант, поэтому я Я начну с этого.
Измените runSim
, чтобы получить список шагов:
runSim :: [Body] -> Double -> [[Body]]
-- now returns a list of bodys, but no terminating condition
runSim bodys numSteps dtparam = nextBodys : runSim nextBodys dtparam
where nextBodys = map (integratePos dtparam . integrateVel dtparam)
(calculateForce bodys)
Теперь main
может выполнить любое количество шагов моделирования и распечатать их:
main = mapM_ (mapM_ print) (take 100 $ runSim [earth, sun] 0.05)
Опять же, я предполагаю, что, следуя совету yairchu, у вас есть Body deriving Show
, так что print
будет работать. mapM_
похож на map
, за исключением того, что для сопоставления требуется монадическая (здесь побочная) функция (заканчивается на M) и не возвращает список (заканчивается на _) . Так что на самом деле это больше похоже на для каждого
в схеме или что-то в этом роде.
Альтернатива - оставить свой runSim
и написать цикл печати, который запускается только по одному шагу за раз:
printLoop :: Integer -> [Body] -> IO [Body]
printLoop 0 bodies = return bodies
printLoop n bodies = do
let planets = runSim bodies 1 0.05
mapM_ print planets -- still need to have Body deriving Show
printLoop (n-1) planets
main = do
printLoop 100 [earth, sun]
return ()
Что касается
printInfo :: Body -> Body
printInfo b = do
putStrLn b
b
Если только " type Body = String
", вы не можете выполнить putStrLn
в Body
.
ghci> :t putStrLn
putStrLn :: String -> IO ()
putStrLn
требует String
. Вы можете использовать putStrLn. покажите
или
$ hoogle "Show a => a -> IO ()"
Prelude print :: Show a => a -> IO ()
используйте print
.
Теперь, делая разумные предположения о типе Body
, printInfo
тип неверен. Поскольку он вызывает putStrLn
, он должен заканчиваться "-> IO Something".
Здесь:
printBody :: Body -> IO Body
printBody b = do
print b
b
Последняя строка здесь неверна. b
относится к типу Body
, но там должны быть данные IO Body
. Как мы можем сделать это преобразование? Использование return :: Monad m => a -> ma
.
Итак, вот рабочая версия:
printBody :: Body -> IO Body
printBody b = do
print b
return b
Для выполнения ввода-вывода вы должны быть в монаде ввода-вывода:
printInfo :: Body -> IO Body
printInfo b = do
putStrLn b
return b
И чтобы вызвать эту функцию из вашей функции runSim
, она должна находиться внутри ввода-вывода. монада:
runSim :: [Body] -> Integer -> Double -> IO [Body]
(Хотя могут быть более эффективные способы организации вашей функции.)
Это дело с монадой нетривиально. Это величайшая сила Haskell, но когда вы впервые сталкиваетесь с ней, трудно осознать ее. Я предлагаю проработать учебное пособие, подобное этому:
В частности, это поможет вам начать:
http://learnyouahaskell.com/input-and-output
Существует множество руководств по монадам, в которых гораздо больше деталей (написание одного - первое, что делают все после того, как они с ними познакомятся). Ссылки с haskell.org - ваши друзья.
Для записи, вот решение, которое я пришел Да ладно, думаю, сейчас я перекодирую его с бесконечными списками, хотя:
runSim :: ([Body], [IO ()]) -> Integer -> Double -> ([Body], [IO ()])
runSim (bodys,bodyActions) 0 dtparam = (bodys, bodyActions)
runSim (bodys,bodyActions) numSteps dtparam = runSim (movedBodys, newBodyActions) (numSteps-1) dtparam
where movedBodys = (map (integratePos dtparam . integrateVel dtparam) (calculateForce bodys))
newBodyActions = bodyActions ++ map print bodys
main = do
let planets = runSim ([earth, sun],[]) 100 0.05
sequence $ snd planets
Еще раз спасибо всем!