2015-04-15 3 views
1

Я пытаюсь решить проблему MINFREE в «Жемчужинах функционального алгоритма» с использованием изменяемых массивов. Проблема заключается в следующем: учитывая список натуральных чисел n, найдите наименьшее натуральное число, которое не отображается в списке. Я уверен, что следующее решение:Очистка кода изменяемой матрицы

minFree2 :: [Int] -> Int 
minFree2 ns = (fromMaybe 0 $ elemIndex True seen) 
    where 
    bound = (1, length ns) 
    seen = elems $ runSTArray $ do 
     arr <- newSTArray bound False 
     mapM_ (\n -> writeArray arr n False) ns 
     return arr 

Но do блока выглядит очень важен для меня, и я хотел бы, чтобы упростить его более функциональный стиль для того, чтобы лучше понять, на монады.

я могу получить, насколько следующее (перезапись только пункт where):

bound = (1, length ns) 
setTrues arr = mapM_ (flip (writeArray arr) False) ns 
seen = elems $ runSTArray $ newSTArray bound False >>= setTrues 

Но это не работает, потому что она возвращает (), а не STArray s i e. Я хотел бы написать что-то вроде:

setTrues arr = mapM_ (flip (writeArray arr) False) ns >> arr 

таким же образом я мог бы написать fn arr => map ....; arr в SML, но это не работает, потому что здесь arr является STArray s i e, а не ST s (STarray s i e). Я бы ожидать, чтобы быть в состоянии исправить, обернув arr обратно в ST, но моя попытка с:

setTrues arr = mapM_ (flip (writeArray arr) False) ns >> arr 
    seen = elems $ runSTArray $ newSTArray bound False >>= return . setTrues 

Вырабатывает ошибку:

No instance for (MArray (STArray s) Bool (STArray s Int)) 
    arising from a use of `setTrues' 

который я не совсем понимаю.

Есть ли хороший способ написать этот код, сводя к минимуму использование do?

+0

Обратите внимание, что это пример в главе 1 «Жемчужины функционального алгоритма Ричарда Берда» (http://www.amazon.com/dp/0521513383), и его результаты показывают, что подход «разделяй и властвуй» использование чистых структур превосходит «accArray». – Carl

ответ

2

Это должно работать:

setTrues arr = mapM_ (flip (writeArray arr) False) ns >> return arr 
seen = elems $ runSTArray $ newSTArray bound False >>= setTrues 

Хитрость выпускающая setTrues возвращение массива вместо (), как вы уже пытались.

+0

Упс. Я чувствую себя немного глупо, забывая об этом. Решение 'Control.Applicative' намного чище, хотя - спасибо, что указали это.Я был бы доволен, если бы 'setTrues' мог быть переписан, чтобы быть беспроблемным, но я подозреваю, что это нельзя сделать разумным. –

+0

На самом деле, я не думаю, что '<*' делает трюк - я получаю 'Не удалось совместить ожидаемый тип' ST s b0 'с фактическим типом 'a0 Int Bool -> m0()'' --- 'setTrues 'не передается STArray как аргумент. –

+0

@PatrickCollins Я написал слишком быстро. '<*' действительно не работает. – chi

1

Насколько я понимаю, одной из главных целей монады ST является обеспечение лазеек для императивного программирования в чисто функциональном коде. Поэтому, я думаю, нет хорошего решения, использующего монаду ST, которая не выглядит слишком настоятельной. Однако, чтобы избавиться от императивного стиля вы можете использовать функцию

accumArray :: Ix i => (e -> a -> e) -> e -> (i, i) -> [(i, a)] -> Array i e 

из Data.Array. Фактически, фактическая реализация этой функции выглядит несколько похожей на ваш код.

В целом, более функциональное решение может выглядеть следующим образом:

minFree2 :: [Int] -> Int 
minFree2 ns = fromMaybe 0 $ elemIndex False $ elems seen 
    where 
    bound = (1, length ns) 
    seen :: Array Int Bool 
    seen = accumArray (||) False bound $ map (,True) ns 

PS: True и False казалось распределены немного неправильно. Я молча зафиксировал это.

+0

Это лучшее решение, но меня интересует одно с использованием изменяемых массивов для моей цели здесь. У меня есть ссылочное решение с 'accArray', с которым я сравниваю это. Возможно, однако, уродство ST неизбежно. –

Смежные вопросы