簡體   English   中英

Haskell可變數組未修改

[英]Haskell Mutable Array not modified

我正在做Euler項目的問題,我在14號

我有一個可變的IOArray來存儲我已經計算出的Collat​​z長度:

import Data.Array.IO
import Control.Monad
import Data.Array

p14 :: IO [Int]
p14 = do
  array <- p14extra
  forM_ [1..1000000] $ \i -> do
    e <- readArray array i
    if e == 0
      then do
        let col  = collatz i
        forM_ col $ \(v,i) -> do
          writeArray array i v
      else return ()
  frozen <- freeze array
  return $ elems frozen

-- an `IOArray` from `1` to `1000000` full of `0`
p14extra :: IO (IOArray Int Int)
p14extra = newArray (1,1000000) 0

collatz :: Int -> [(Int, Int)]
collatz n
  | n == 1    = [(1,1)]
  | otherwise = (n, (snd $ head hack) + 1) : hack
  where
    hack = collatz $ if even n then (n `div` 2) else (3 * n + 1)

其中第一個元素是要計算的數字,第二個數字是其Collat​​z序列的長度。

事實是,在p14我執行writeArray array iv但它始終包含一個零(0s)數組。 這是為什么?

我運行了您的確切代碼,發現數組更改。

由於結尾處有很多零,因此您需要一直滾動到開頭以查找數字。

實際上,如果您替代

  frozen <- freeze array
  return $ elems frozen

  sol <- getElems array
  return (length . takeWhile (/=0) $ sol)

您將獲得525,這是正確的解決方案……更多有關該解決方案的說明。

但是有兩個問題:

  1. forM_ col $ \\(v,i) -> do ,根據我的理解, (v,i)應該是(i,v) ,就像collat​​z一樣,第一個值是索引,第二個是值。
  2. 現在,我們已經解決了這個問題,如果嘗試使用forM_ [1..10]newArray (1,10)運行代碼, forM_ [1..10]得到*** Exception: Ix{Int}.index: Index (16) out of range ((1,10)) 一旦將值再次設置為1000000 ,也會發生這種情況,因為在某些情況下,為了計算數字的collat​​z,必須計算大於1000000的數字的collat​​z。 例如,的在Collat​​z 837798包括的計算1885048后僅4個步驟。 嘗試寫入該索引會破壞事情。

對於第二種解決方案,您可以:

  • 嘗試制作一個絕對巨大的可變數組,以希望您的collat​​z序列永遠不會超出范圍(我不建議這樣做)
  • 嘗試制作一個不利用預計算值的朴素版本(主要是使其正常運行,然后從那里進行適當優化)
  • 擁有它,以便在每次寫入之前執行綁定檢查
  • ...或者從頭開始重新設計事物,看看是否可以提出更優雅的解決方案;)

length . takeWhile (/=0) $ sol之所以如此length . takeWhile (/=0) $ sol length . takeWhile (/=0) $ sol工作原理是,因為您使用了(v,i)而不是(i,v) ,所以您在索引i中寫入了具有collat​​z序列長度i最低編號 ,因此在索引525上得到了837799,然后從那里開始都是零,因為沒有collat​​z序列大於525。

我不知道您的意思是“不修改”。

使用此代碼(請注意,我已經更改了迭代和數組范圍):

import Control.Monad (forM_)
import Data.Array.IO
import Data.Array

p14 :: IO [Int]
p14 = do
  array <- p14extra
  forM_ [1..10] $ \i -> do
    e <- readArray array i
    if e == 0
      then do
        let col  = collatz i
        forM_ col $ \(v,i) -> do
          writeArray array i v
      else return ()
  frozen <- freeze array
  return $ elems frozen

p14extra :: IO (IOArray Int Int)
p14extra = newArray (1,100) 0

collatz :: Int -> [(Int, Int)]
collatz n
  | n == 1    = [(1,1)]
  | otherwise = (n, (snd $ head hack) + 1) : hack
  where
    hack = collatz $ if even n then (n `div` 2) else (3 * n + 1)

ghci運行p14產生:

[1,2,4,8,16,5,10,20,40,13,26,52,17,34,11,22,7,14,28,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

萬一這是使您感到困惑的直覺:盡管p14修改了它使用的數組,但p14extra不再包含該數組。 這是一個IO操作,每次運行時都會始終創建一個填充有零的數組。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM