[英]Haskell (formidably long) palindrome check
I'm working my way up the infamous H-99 problems and I'm playing around problem #6 (find out whether a list is a palindrome). 我正在努力解决臭名昭著的H-99问题,并且在解决#6问题 (找出列表是否是回文)。 I understand most solutions will work on reasonably well on reasonably short lists.
我知道大多数解决方案在合理的简短列表上都能很好地工作。 Now how would I write a function to test if a very long list is a palindrome, eg,
现在,我将如何编写一个函数来测试很长的列表是否是回文,例如,
let ll = [1..10000000] ++ [10000000, 10000000-1..1] ++ [7]
I (naively perhaps) tried to test it as such: 我(天真的)试图这样测试:
isPalindrome [] = True
isPalindrome [_] = True
isPalindrome [x,y] = x==y
isPalindrome (x:xs) = isPalindrome [x,last xs] && (isPalindrome $ init xs)
where I'm assuming that if isPalindrome [x,last xs]
evaluates to False
, the expensive right hand side of &&
will not be evaluated. 我假设如果
isPalindrome [x,last xs]
为False
,则&&
的昂贵右侧将不会被评估。
I profiled the above and here's what it yielded: 我概述了上面的内容,结果如下:
Mon Jun 30 18:33 2014 Time and Allocation Profiling Report (Final)
h99 +RTS -p -RTS
total time = 1.29 secs (1292 ticks @ 1000 us, 1 processor)
total alloc = 2,720,050,200 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
main Main 95.6 100.0
isPalindrome Main 4.4 0.0
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 43 0 0.0 0.0 100.0 100.0
CAF Main 85 0 0.0 0.0 100.0 100.0
main Main 86 1 95.6 100.0 100.0 100.0
isPalindrome Main 87 2 4.4 0.0 4.4 0.0
CAF GHC.Conc.Signal 84 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding 77 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding.Iconv 75 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Handle.FD 68 0 0.0 0.0 0.0 0.0
CAF GHC.Show 63 0 0.0 0.0 0.0 0.0
from which I infer that the problem is with last xs
which could require the whole xs
to be computed. 从中我推断出问题出在
last xs
,这可能需要计算整个xs
。 If so, is there a workaround? 如果是这样,是否有解决方法? Or is just the implementation of
[a..b]
that is greedy? 还是只是
[a..b]
实现是贪婪的?
Thanks. 谢谢。
As you surmise, last xs
is the problem - it'll take linear time in the length of the list, and force the entire list to be generated immediately ( [a..b]
is lazy, as with Haskell lists in general). 如您所料,
last xs
是个问题-它会在列表的长度上花费线性时间,并迫使立即生成整个列表( [a..b]
是惰性的,通常与Haskell列表一样)。 So your isPalindrome
function will be quadratic time in total. 因此,您的
isPalindrome
函数总共将是二次时间。
For a naive implementation, I would start with xs == reverse xs
, which has the right asymptotic behaviour (linear) but a relatively high constant factor, and will end up having two entire copies of the list in memory at the point that reverse
gets to the end of the list and starts producing results. 对于简单的实现,我将以
xs == reverse xs
,它具有正确的渐近行为(线性),但是常数系数相对较高,最终将在reverse
获取点的内存中拥有列表的两个完整副本。到列表末尾并开始产生结果。
You can probably improve on that approach somewhat by looking at the length
and then splitting the list at the halfway point, or maybe doing something cleverer in a single pass. 您可以通过查看
length
然后在中途分割列表,或者在单遍操作中做一些更巧妙的操作来对这种方法进行某种程度的改进。
However I think for substantially better behaviour, you'd need to switch data structures - I'd look at something like Data.Deque
or Data.Sequence
. 但是,我认为为了获得更好的性能,您需要切换数据结构-我将研究诸如
Data.Deque
或Data.Sequence
类的东西。
I think it is the last xs
, as you suspect. 我认为这是
last xs
,您怀疑。 Suggestion: before you do any recursion, split the string at the midpoint*, and reverse the second half. 建议:在执行任何递归操作之前,请在中点处分割字符串*,然后反转下半部分。 Then you're comparing the first character of both strings each time you recurse.
然后,您在每次递归时都比较两个字符串的第一个字符。 *If the strings of odd length, both strings should include the middle character.
*如果字符串长度不固定,则两个字符串都应包含中间字符。
A bunch of interesting palindrome functions were suggested on this reddit thread : 建议在此reddit线程上使用一系列有趣的回文功能:
Naive 幼稚
palindrome list = reverse list == list
Pointfree 无点
palindrome = liftM2 (==) reverse id
Applicative 适用性
palindrome = (==) <$> reverse <*> id
Monadic 莫纳迪奇
palindrome = reverse >>= (==)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.