Haskell 生成此数组时执行无限循环
我实现了KMP算法的失败表Haskell 生成此数组时执行无限循环,haskell,Haskell,我实现了KMP算法的失败表 kmp s = b where a = listArray (0,length s-1) s b = 0:list 0 (tail s) list _ [] = [] list n (x:xs) | x==a!n = (n+1):list (n+1) xs | n > 0 = list (b!!(n-1)) (x:xs)
kmp s = b
where a = listArray (0,length s-1) s
b = 0:list 0 (tail s)
list _ [] = []
list n (x:xs)
| x==a!n = (n+1):list (n+1) xs
| n > 0 = list (b!!(n-1)) (x:xs)
| otherwise = 0:list 0 xs
b
是一个列表,b!!(n-1)
最后一行速度较慢,因此我希望加快速度,并执行了以下操作
kmp s = b
where a = listArray (0,length s-1) s
t = listArray (0,length s-1) b
b = 0:list 0 (tail s)
list _ [] = []
list n (x:xs)
| x==a!n = (n+1):list (n+1) xs
| n > 0 = list (t!(n-1)) (x:xs)
| otherwise = 0:list 0 xs
注:唯一的区别是更换b代码>通过t
并声明t
为从b
生成的数组
对于相同的输入,原始代码有正确的输出,但新代码只输出
如何解决此问题?您的问题是列表b
需要数组t
来确定其结构(长度)。但是数组t
需要列表的长度才能存在:
listArray :: Ix i => (i,i) -> [e] -> Array i e
listArray (l,u) es = runST (ST $ \s1# ->
case safeRangeSize (l,u) of { n@(I# n#) ->
case newArray# n# arrEleBottom s1# of { (# s2#, marr# #) ->
let fillFromList i# xs s3# | i# ==# n# = s3#
| otherwise = case xs of
[] -> s3#
y:ys -> case writeArray# marr# i# y s3# of { s4# ->
fillFromList (i# +# 1#) ys s4# } in
case fillFromList 0# es s2# of { s3# ->
done l u n marr# s3# }}})
如您所见,首先分配一个适当大小的原始数组,然后用arrelbottom
(这是一个错误
调用,消息为undefined array element),然后遍历列表并将列表元素写入数组(列表元素可以毫无问题地引用数组值)。最后,数组被冻结。在冻结数组之前,不能从填充代码外部访问它(它基本上是一个sts
计算)
最简单的修复方法是在sts
monad中使用可变数组
kmp s = elems b
where
l = length s - 1
a = listArray (0, l) s
b = runSTArray $ do
t <- newArray_ (0,l)
writeArray t 0 0
let fill _ _ [] = return t
fill i n (x:xs)
| x == a!n = do
writeArray t i (n+1)
fill (i+1) (n+1) xs
| n > 0 = do
k <- readArray t (n-1)
fill i k (x:xs)
| otherwise = do
writeArray t i 0
fill (i+1) 0 xs
fill 1 0 (tail s)
kmp s=elems b
哪里
l=长度s-1
a=listArray(0,l)s
b=星光大道$do
t0=do
k您的问题是列表b
需要数组t
来确定其结构(长度)。但是数组t
需要列表的长度才能存在:
listArray :: Ix i => (i,i) -> [e] -> Array i e
listArray (l,u) es = runST (ST $ \s1# ->
case safeRangeSize (l,u) of { n@(I# n#) ->
case newArray# n# arrEleBottom s1# of { (# s2#, marr# #) ->
let fillFromList i# xs s3# | i# ==# n# = s3#
| otherwise = case xs of
[] -> s3#
y:ys -> case writeArray# marr# i# y s3# of { s4# ->
fillFromList (i# +# 1#) ys s4# } in
case fillFromList 0# es s2# of { s3# ->
done l u n marr# s3# }}})
如您所见,首先分配一个适当大小的原始数组,然后用arrelbottom
(这是一个错误
调用,消息为undefined array element),然后遍历列表并将列表元素写入数组(列表元素可以毫无问题地引用数组值)。最后,数组被冻结。在冻结数组之前,不能从填充代码外部访问它(它基本上是一个sts
计算)
最简单的修复方法是在sts
monad中使用可变数组
kmp s = elems b
where
l = length s - 1
a = listArray (0, l) s
b = runSTArray $ do
t <- newArray_ (0,l)
writeArray t 0 0
let fill _ _ [] = return t
fill i n (x:xs)
| x == a!n = do
writeArray t i (n+1)
fill (i+1) (n+1) xs
| n > 0 = do
k <- readArray t (n-1)
fill i k (x:xs)
| otherwise = do
writeArray t i 0
fill (i+1) 0 xs
fill 1 0 (tail s)
kmp s=elems b
哪里
l=长度s-1
a=listArray(0,l)s
b=星光大道$do
t0=do
k这并没有直接回答您的问题,但提供了一个比使用STArray
s的建议版本更简单的修复
该算法的命令式版本直接转换为不使用重复列表索引或状态的版本,只使用惰性数组构造:
import Data.Array.IArray
kmp :: String -> Array Int Int
kmp s = b
where
a :: Array Int Char
a = listArray (0,l-1) s
b = listArray (1,l) (0:map (\q -> f (b!(q-1)) q) [2..l])
f k q
| k > 0 && (a ! k) /= (a ! (q-1)) =
f (b ! k) q
| a ! k == a ! (q-1) = k + 1
| otherwise = k
l = length s
但是,我还没有对此进行基准测试。这并没有直接回答您的问题,但提供了一个比使用STArray
s的建议版本更简单的修复方法
该算法的命令式版本直接转换为不使用重复列表索引或状态的版本,只使用惰性数组构造:
import Data.Array.IArray
kmp :: String -> Array Int Int
kmp s = b
where
a :: Array Int Char
a = listArray (0,l-1) s
b = listArray (1,l) (0:map (\q -> f (b!(q-1)) q) [2..l])
f k q
| k > 0 && (a ! k) /= (a ! (q-1)) =
f (b ! k) q
| a ! k == a ! (q-1) = k + 1
| otherwise = k
l = length s
但是,我还没有对此进行基准测试。教科书中的强制算法版本(例如,请参见CLRS)直接转换为快速版本(无O(n^2)
lookups),只使用数组的惰性构造。另外,伯德的书《函数算法设计的珍珠》中有一个KMP的衍生版本,它不使用数组,但这看起来肯定与OP的算法有很大的不同。@Fixnum在STArray
版本中没有O(n²),这是我所知的教科书算法的一个未经优化的版本,完全线性。当然,以后将数组转换为列表不是很有效,应该使用数组[实际上,从一开始就应该使用未绑定的数组]。对不起!我指的是OP对列表索引的抱怨,我错误地认为这会导致渐进式的减速。我只是想指出,您可以消除(!!)
,而无需可变数组和对OP的代码进行侵入性较小的更改)。我明白了。对不起,误会了。但是我没有看到OP算法的入侵性改变,它是一个非常直接的端口,我不想改变结构。教科书中的命令式算法版本(例如,CLRS)直接转换为快速版本(noO(n^2)
lookups),只使用数组的惰性构造。另外,伯德的书《函数算法设计的珍珠》中有一个KMP的衍生版本,它不使用数组,但这看起来肯定与OP的算法有很大的不同。@Fixnum在STArray
版本中没有O(n²),这是我所知的教科书算法的一个未经优化的版本,完全线性。当然,以后将数组转换为列表不是很有效,应该使用数组[实际上,从一开始就应该使用未绑定的数组]。对不起!我指的是OP对列表索引的抱怨,我错误地认为这会导致渐进式的减速。我只是想指出,您可以消除(!!)
,而无需可变数组和对OP的代码进行侵入性较小的更改)。我明白了。对不起,误会了。但是我没有看到OP算法的侵入性改变,它是一个非常直接的端口,我不想改变结构。