1

我试图实现标题所说的,但由于懒惰的评估,我无法得到结果:

data AlgorithmM = AlgorithmM {fm::[Int],fa::[Int],fj::Int,fn::Int}
m1a :: AlgorithmM->(IO (),AlgorithmM)
m1a (AlgorithmM m a j n) = (return (),AlgorithmM (2:m) a j n)

m1b :: AlgorithmM->(IO (),AlgorithmM)
m1b (AlgorithmM m a j n) = (return (),AlgorithmM m (take n $! repeat 0) j n)


m2 algo = (visit False $ fa algo,algo)
  where visit True l =do
             mapM (\z->putStr $ show z) l
             putStr "\n"
    visit False (x:xs) = do
                          mapM (\z->putStr $ show z) xs
                          putStr "\n"
initN :: AlgorithmM->(IO (),AlgorithmM)
initN (AlgorithmM m a j n) = (return (),AlgorithmM m a j ((length m)-1))

m3 :: AlgorithmM->(IO (),AlgorithmM)
m3 (AlgorithmM m a j n) = (return (),AlgorithmM m a n n)


m4 :: AlgorithmM->(IO (),AlgorithmM)
m4 (AlgorithmM m a j n) = if (a !! j) == (m !! j) - 1 then m4 (AlgorithmM m (setajZero a j) (j-1) n) else  (return (),AlgorithmM m a j n)
 where setajZero (x:xs) 0 = 0:xs
       setajZero (x:xs) j = x:(setajZero xs (j-1))

m5 :: AlgorithmM->(IO (),AlgorithmM)
m5 (AlgorithmM m a j n) = if j==0 then (return (),AlgorithmM m a j n) else m2 (AlgorithmM m a j n)

bind :: (IO(),AlgorithmM)->(AlgorithmM->(IO (),AlgorithmM))->(IO(),AlgorithmM)
bind g f = f $! snd g 

testAlgorithmM = m1a (AlgorithmM [2,2,2] [] 0 0) `bind` initN `bind` m1b `bind` m2 `bind` m3 `bind` m4 `bind` m5

main = do
    let (x,y) = testAlgorithmM
    x

当我将上面的代码运行到解释器时,我采取

例外:前奏曲。(!!):索引太大

我认为 m1a 中的列表没有扩展 n+1 所以 m4 抛出异常

有任何想法吗?谢谢。

4

3 回答 3

2

正如我上面的海报所说,你不正确地索引了你的列表。替换(a !! j) == (m !! j) - 1(a !! (j - 1)) == (m !! (j - 1)) - 1,它将起作用。

但是,您所有多余的return ()陈述都无济于事。看来您对 monads 的作用有误解,特别是IO. 您似乎也相信懒惰会使您的代码无法正常工作;虽然这在技术上是正确的,但问题不在于 haskell 是懒惰的,而是你没有告诉它你想要计算什么。

我修改了代码以消除所有return ()语句。请注意,我没有更改代码的执行。它将以与原始顺序完全相同的顺序执行所有内容。

data AlgorithmM = AlgorithmM {fm::[Int],fa::[Int],fj::Int,fn::Int} deriving Show

m1a :: AlgorithmM -> AlgorithmM
m1a (AlgorithmM m a j n) = AlgorithmM (2:m) a j n

m1b :: AlgorithmM -> AlgorithmM
m1b (AlgorithmM m a j n) = AlgorithmM m (take n $! repeat 0) j n

m2 :: AlgorithmM -> (String, AlgorithmM)
m2 algo = (visit False $ fa algo,algo)
  where visit True l = (concatMap show l) ++ "\n"
        visit False (x:xs) = concatMap show xs ++ "\n"

initN :: AlgorithmM -> AlgorithmM
initN (AlgorithmM m a j n) = AlgorithmM m a j ((length m)-1)

m3 :: AlgorithmM-> AlgorithmM
m3 (AlgorithmM m a j n) = AlgorithmM m a n n


m4 :: AlgorithmM -> AlgorithmM
m4 (AlgorithmM m a j n) = if (a !! (j - 1)) == (m !! (j - 1)) - 1 then m4 (AlgorithmM m (setajZero a j) (j-1) n) else AlgorithmM m a j n
 where setajZero (x:xs) 0 = 0:xs
       setajZero (x:xs) j = x:(setajZero xs (j-1))

m5 :: AlgorithmM -> (String, AlgorithmM)
m5 (AlgorithmM m a j n) = if j==0 then ("", AlgorithmM m a j n) else m2 (AlgorithmM m a j n)

testAlgorithmM = s0
    where (s0, a) = m2 $ m1b $ initN $ m1a (AlgorithmM [2,2,2] [] 0 0)
          b       = m5 $ m4 $ m3 a

如果你检查m2你会发现你visit False每次都打电话。您可以进一步减少到

m2 :: AlgorithmM -> (String, AlgorithmM)
m2 algo = ((\xs -> concatMap show xs ++ "\n") $ tail $  fa algo,algo)

我只是简单地将函数替换为visit对应的函数分支False

然后是发生了什么的问题testAlgorithmM。同样,执行是相同的(我刚刚删除了严格性属性)。但请注意,由于值为testAlgorithmMis s0,因此不会计算m5 $ m4 $ m3 a,因为不需要它的值来产生所需的输出。使用您的原始代码是不可能看到这种情况发生的,但是当您删除所有混淆时,它是非常明显的。

在函数应用程序链的中间创建一个字符串似乎是一种保存一些中间状态的方法。如果是这种情况,您应该查看ST monadIO除了你不能printputStrLn. 但是,您可以在执行完所有计算后执行这些操作。

如果要计算整个算法转换链,则必须执行以下操作:

testAlgorithmM = (b,s0)
    where (s0, a) = m2 $ m1b $ initN $ m1a (AlgorithmM [2,2,2] [] 0 0)
          b       = m5 $ m4 $ m3 a

如果您想查看它,这也会输出您的中间字符串值。

于 2013-10-06T21:20:20.950 回答
1

首先,懒惰永远不应该对纯函数的价值产生可观察到的影响(除非是高度病态的情况)。所以列表索引错误不能是太懒惰的结果。

假设我们从表面上看我们的错误信息,并猜测错误来自直接m4调用的Prelude.!!。让我们使用GHCi 的调试器

ghci> :break m4
ghci> :step main
ghci> :continue
Stopped at foo.hs:(24,1)-(26,50)
ghci> :step
Stopped at foo.hs:24:27-137
_result :: (IO (), AlgorithmM) = _
a :: [Int] = _
j :: Int = _
m :: [Int] = _
n :: Int = _
ghci> :list
23  m4 :: AlgorithmM->(IO (),AlgorithmM)
24  m4 (AlgorithmM m a j n) =
      if (a !! j) == (m !! j) - 1
         then m4 (AlgorithmM m (setajZero a j) (j-1) n)
         else  (return (),AlgorithmM m a j n)
25   where setajZero (x:xs) 0 = 0:xs
ghci> :force a j m n
a = [0,0,0]
j = 3
m = [2,2,2,2]
n = 3

猜猜接下来会发生什么?

于 2013-10-06T21:15:16.937 回答
0

该错误意味着您的索引超出了列表的末尾;与懒惰无关。看一下实现

您的实现显然是错误的。也许你只是证明它是正确的?

于 2013-10-06T20:54:43.023 回答