简体   繁体   English

Haskell(相当长)回文检查

[英]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.DequeData.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM