2014-10-15 3 views
2

나는 STArray가 어떻게 작동하는지 배우려고했으나 할 수 없었다. (의사는 가난하거나 적어도 내가 찾은 사람이 아닙니다.)1) 배열을 사용하여 2) 목록 연결을 피하십시오 (지연 목록?)?

어쨌든 나는 다음 알고리즘을 가지고 있지만 많이 사용합니다!는 느립니다. 어떻게 STArray 모나드를 사용하도록 변환 할 수 있습니까?

-- The Algorithm prints the primes present in [1 .. n] 

main :: IO() 
main = print $ primesUpTo 100 

type Nat = Int 

primesUpTo :: Nat -> [Nat] 
primesUpTo n = primesUpToAux n 2 [1] 

primesUpToAux :: Nat -> Nat -> [Nat] -> [Nat] 
primesUpToAux n current primes = 
    if current > n 
    then primes 
    else primesUpToAux n (current + 1) newAcum 
    where newAcum = case isPrime current primes of 
        True -> primes++[current] 
        False -> primes 

isPrime :: Nat -> [Nat] -> Bool 
isPrime 1 _ = True 
isPrime 2 _ = True 
isPrime x neededPrimes = isPrimeAux x neededPrimes 1 

isPrimeAux x neededPrimes currentPrimeIndex = 
    if sqrtOfX < currentPrime 
    then True 
    else if mod x currentPrime == 0 
     then False 
     else isPrimeAux x neededPrimes (currentPrimeIndex + 1) 
    where 
     sqrtOfX = sqrtNat x 
     currentPrime = neededPrimes !! currentPrimeIndex 

sqrtNat :: Nat -> Nat 
sqrtNat = floor . sqrt . fromIntegral 

편집

아차는! 문제가 아니었다. 다음 버전의 알고리즘 (아래)에서 나는 !!의 사용을 제거했습니다; 또한, 나는 @pedrorodrigues

main :: IO() 
main = print $ primesUpTo 20000 

type Nat = Int 

primesUpTo :: Nat -> [Nat] 
primesUpTo n = primesUpToAux n 1 [] 

primesUpToAux :: Nat -> Nat -> [Nat] -> [Nat] 
primesUpToAux n current primesAcum = 
    if current > n 
    then primesAcum 
    else primesUpToAux n (current + 1) newPrimesAcum 
    where newPrimesAcum = case isPrime current primesAcum of 
          True -> primesAcum++[current] 
          False -> primesAcum 

isPrime :: Nat -> [Nat] -> Bool 
isPrime 1 _ = False 
isPrime 2 _ = True 
isPrime x neededPrimes = 
    if sqrtOfX < currentPrime 
    then True 
    else if mod x currentPrime == 0 
     then False 
     else isPrime x restOfPrimes 
    where 
      sqrtOfX = sqrtNat x 
      currentPrime:restOfPrimes = neededPrimes 

sqrtNat :: Nat -> Nat 
sqrtNat = floor . sqrt . fromIntegral 

에 의해 지적, 아닌 프라임 지금이 질문은 정말 약 2 질문입니다 인 1 고정 : 배열을 사용하려면이 알고리즘을 변형하는 방법

1 .- 목록 대신? (하스켈에서 상태와 배열을 다루는 법을 배우기위한 것입니다.) 누군가 이미 코멘트에 답변했지만,별로 좋지 않은 설명이있는 예를 가리키고 있습니다.

2.- 새로운 소수가 발견 될 때마다 목록의 연결을 제거하는 방법은 무엇입니까?

진정한 ->은 [전류]

+4

여기에서는 알고리즘의 단순한 평가판 인 STArray가 필요하지 않습니다. 여러분은 다른 것들보다 먼저 표준 목록 조작 함수 ('filter','takeWhile','all','any' 등)를보아야합니다, 왜냐하면 문제는 그것들을 사용하여 최적으로 그리고 관용적으로 해결 될 수 있기 때문입니다. 꽤 우회로를 만들고있어. –

+3

기능적 스타일에서 빠른 구현을 원하십니까? 이전의 코멘트를보고 (그리고 목록에 의해 맹목적으로 표현 세트의 잘못을 피하십시오). 또는 STArray를 사용하는 방법을 배우고 싶다면 - STArray의 문제점을 정확하게 설명하십시오. 그렇다면 "필수"코드를 작성해야한다는 것을 알고 있습니다 (모든 것이 ST 모나드에 있음). – d8d0d65b3f7cf42

+3

1 그런데, 소수되지 않습니다. –

답변

1

여기에 귀하의 코드를 더 많거나 적은 직접 번역 정수의 언 박싱 배열 작업에있어 ++ primesAcum :

import Control.Monad 
import Control.Monad.ST 
import Data.Array.ST 
import Data.Array.Unboxed 
import Control.Arrow 

main :: IO() 
main = print . (length &&& last) . primesUpTo $ 1299709 

primesUpTo :: Int -> [Int] 
primesUpTo = takeWhile (> 0) . elems . primesUpToUA 

primesUpToUA :: Int -> UArray Int Int 
primesUpToUA n = runSTUArray $ do 
    let k = floor(1.25506 * fromIntegral n/log (fromIntegral n)) -- WP formula 
    ar <- newArray (0,k+1) 0   -- all zeroes initially, two extra spaces 
    let 
    loop c i | c > n = return ar   -- upper limit is reached 
      | otherwise = do    -- `i` primes currently in the array 
     b <- isPrime 0 i c    -- is `c` a prime? 
     if b then do { writeArray ar i c ; loop (c+1) (i+1) } 
     else loop (c+1) i 
    isPrime j i c | j >= i = return True -- `i` primes 
        | otherwise = do   -- currently in the array 
      p <- readArray ar j 
      if p*p > c   then return True 
      else if rem c p == 0 then return False 
      else     isPrime (j+1) i c 
    loop 2 0 

이가 더 많거나 그다지 자명하지 않습니다. 한 번에 한 문장 씩 천천히 읽으십시오.

배열을 사용하면 목록이 없으므로 목록 연결에 문제가 없습니다. 우리는 새로운 항목을 추가 할 때 소수의 배열을 사용합니다.

물론 목록 기반 코드를 다시 작성하여 더 잘 작동 할 수 있습니다. 방법, 마지막에 명시 적으로을 추가하는 을 아니지만, 목록 — 생산되는 방식을 정의하고 게으른 수 있도록 - the simplest re-write는 키가 corecursive 생각에 재귀 적 사고로 전환하는 것입니다

ps :: [Int] 
ps = 2 : [i | i <- [3..], 
       and [rem i p > 0 | p <- takeWhile ((<=i).(^2)) ps]] 
primesTo n = takeWhile (<= n) ps 

입니다 의미론은 나머지를 처리합니다.

+0

고마워. 한 가지 질문은 newArray (0, k + 1) 0이 ST (STUArray 's Int Int)를 생성한다는 것입니다. 맞습니까? s (주)의 유형은 무엇입니까? 나는 isPrime의 유형을 알아 내지 못했습니다. –

+0

을 클릭하면 모든 항목의 유형을 볼 수 있습니다 (예 : 'isPrime'에서'let x = isPrime == True'를 추가하고'Bool'과 알고 싶은 타입 사이의 타입 불일치에 대한 에러 메시지를 읽습니다. - 우리는'runSTUArray :: Ix i => (for s ST (STUArray sie)) -> UArray ie'를 알고 있으므로'do' 표현식은':: Ix i => (for s입니다. s (STUArray sie))'. [ "ss 유형의 계산은 s로 인덱싱 된 내부 상태를 변환하고 a 유형의 값을 반환합니다."] (http://www.haskell.org/ghc/docs/7.6.2/html/libraries/base- 4.6.0.1/Control-Monad-ST-Safe.html#t:ST). (... cont) –

+0

(... contd) 그래서 표현식에서의 계산은'ST s' 모나드에서입니다. 's'는'forall s'에 의해 캡슐화되므로 우리는 그것이 무엇인지 알지 못합니다. [newArray :: Ix i => (i, i) -> e -> m (aie)'] (http://www.haskell.org/ghc/docs/7.6.2/html/libraries/array- 0.4.0.1/Data-Array-MArray.html#t:MArray) 'm ~ ST s'와 보이지 않는's' 및'arr :: aie ~ STUArray sie' (왜냐하면 우리는 * 모나드 계산); 'e'는'Int'이고'i'는''Ix'' - 원거리 색인 (http://www.haskell.org/ghc/docs/7.6.2/html/libraries/base-4.6.0.1)에 있습니다. /Data-Ix.html#t:Ix), (Int, Int)와 같습니다. –