2016-02-14 4 views
1

Я пишу программу в Haskell для имитации боя между двумя игроками в D & D. Метод, который я использую, заключается в том, чтобы хранить игроков как Карты, а затем выполнять «атакующие» функции, которые принимают Player и вернуть IO Player с обновленным здоровьем, исходя из того, связана ли атака или нет. Весь мой код, кажется, работает по назначению, вплоть до последней функции, combat. combat должен взять двух игроков, первым атаковать второй, проверить, мертв ли ​​второй, а если нет, повторите попытку с изменением порядка игроков. Вместо этого он, кажется, чередуется бесконечно.Haskell бесконечный цикл в блоке do

Сама функция выглядит следующим образом -

combat :: Player -> Player -> IO String 
combat a b = do 
    damaged <- a `attacks` b 
    nextRound <- combat damaged a 
    return $ if (victor a damaged) /= "No victor yet" then (victor a damaged) else nextRound 

attacks функция принимает два игрока, проверяет идентификатор первого, и применяет соответствующую последовательность атак на второй. Он отлично работает, когда я использую его самостоятельно в GHCi, поэтому я не думаю, что там есть проблема.

Функция victor принимает двух игроков, проверяет, является ли кто-либо из них мертвым (определяется как HP < = 0) и возвращает строку, описывающую результат. Когда я написал игрушечную версию combat без какой-либо рекурсии, которая только что вернула «тест», когда условие не было выполнено, оно возвращало комбинацию «Player 1 выигрывает!». и «тест», поэтому я не думаю, что там есть проблема.

я наткнулся на подобный взгляд вопрос с LET привязок, где кто-то работает в бесконечном цикле, когда они имели такой код let n = n + 1, но я не уверен, что если <- работает таким же образом (или, если это было , как еще я мог бы написать его).

+0

Этот код никогда не дойдет до строки 'return'.Каждый раз, когда вы вызываете «битву», он будет называть себя аргументами в обратном порядке, но никогда не будет завершен, потому что он снова вызовет себя, прежде чем сможет пройти мимо этой линии. –

+0

@PeterHall Хорошо, это имеет смысл. Но, я получаю ошибку типа, когда я поместил «боевое повреждение» исключительно в оператор else (он хочет регулярную String, но он получает строку IO), которая является проблемой, которую я пытался решить, когда я положил рекурсию выше оператор if. – Kumarbis

ответ

2

Как уже упоминалось в комментариях, что вам нужно сделать проверку завершения, прежде чем сделать рекурсивный шаг, или вы никогда не будете в состоянии прекратить. Вот небольшое переназначение:

combat :: Player -> Player -> IO String 
combat a b = do 
    damaged <- a `attacks` b 
    case victor a damaged of 
     "No victor yet" -> combat damaged a 
     winner -> return winner 

Обратите внимание, что это было бы гораздо лучше, если victor вернулся Maybe String вместо этого, так что вы не будете зависеть от этой волшебной строки "No victor yet". Тогда вы можете написать:

combat :: Player -> Player -> IO String 
combat a b = do 
    damaged <- a `attacks` b 
    case victor a damaged of 
     Nothing -> combat damaged a 
     Just winner -> return winner 
1

Пожалуйста, постарайтесь опубликовать минимальные примеры компиляции в будущем.

Как указано в комментариях, по телефону combat безоговорочно изнутри корпуса combat является не стартером. Вместо этого погрузите его в правильную руку if-then-else.

Вы также упомянули, что у вас была ошибка типа при размещении combat в одном из случаев. Я предполагаю, что вы использовали return $ if ..., что означает, что любая ветка будет завернута в другой слой IO ..., но ваш combat уже относится к типу IO _.

combat :: Player -> Player -> IO String 
combat a b = do 
    damaged <- a `attacks` b 
    if (victor a damaged) /= "No victor yet" 
     then return (victor a damaged) 
     else combat damaged a 

Теперь есть еще два бородавки. Одним из них является повторное заявление, вызывающее victor. Другой использует сравнение строк для определения потока программы, тьфу! Вместо этого как насчет того, чтобы вы сделали victor, верните ADT?

data Status = Dead String | Alive 

combat :: Player -> Player -> IO String 
combat a b = do 
    damaged <- a `attacks` b 
    case victor a damaged of 
     Alive -> combat damaged a 
     Dead s -> return s 
Смежные вопросы