通过递归检查尾部来迭代列表的性能

时间:2017-01-13 16:39:33

标签: performance haskell recursion clojure

我决定尝试通过做一些CodinGame挑战来学习Haskell(所以这个问题是超级初学者级别的东西,我确定)。其中一个需要在整数列表中搜索任意两个值之间的最小差异。我之前在Clojure中解决了这个问题:

(ns Solution
  (:gen-class))

(defn smallest-difference [values]
  (let [v (sort values)]
    (loop [[h & t] v curr-min 999999]
      (if (nil? t) curr-min
        (let [dif (- (first t) h)]
          (recur t (if (> curr-min dif) dif curr-min)))))))

(defn -main [& args]
  (let [horse-strengths (repeatedly (read) #(read))]
      (let [answer (smallest-difference horse-strengths)]
        (println answer)))) 

我尝试在Haskell中实现相同的解决方案,具体如下:

readHorses :: Int -> [Int] -> IO [Int]
readHorses n h
    | n < 1 = return h
    | otherwise = do
        l <- getLine
        let hn = read l :: Int
        readHorses (n - 1) (hn:h)

findMinDiff :: [Int] -> Int -> Int
findMinDiff h m
    | (length h) < 2    = m
    | (h!!1 - h!!0) < m = findMinDiff (tail h) (h!!1 - h!!0)
    | otherwise         = findMinDiff (tail h) m



main :: IO ()
main = do
    hSetBuffering stdout NoBuffering -- DO NOT REMOVE
    input_line <- getLine
    let n = read input_line :: Int
    hPrint stderr n
    horses <- readHorses n []
    hPrint stderr "Read all horses"
    print (findMinDiff (sort horses) 999999999)
    return ()

对于大型输入(99999值)的超时,其中Clojure解决方案没有。然而,它们看起来与我很相似。

至少从表面上看,阅读价值观和构建列表并不是问题,因为&#34;阅读所有马匹&#34;在超时前打印。

如何使Haskell版本更高效?

2 个答案:

答案 0 :(得分:5)

您在length的每次递归中计算列表的findMinDiff。由于length需要O(n)时间findMinDiff需要O(n^2)次,而不是O(n)

您可以使用pattern matching代替length!!tail

编写相同的内容
findMinDiff :: [Int] -> Int -> Int
findMinDiff (h0 : hs@(h1 : _)) m = 
    if h1 - h0 < m
    then findMinDiff hs (h1 - h0)
    else findMinDiff hs m
findMinDiff _ m = m

答案 1 :(得分:4)

顺便说一下,完全替代的实现可以写成如下。 (伪代码如下)

列出

h = [h0, h1, h2 ...

删除一个元素

drop 1 h = [h1, h2, h3 ...

计算逐点差异

zipWith (-) (drop 1 h) h = [h1-h0, h2-h1, h3-h2, ...

然后采取最低限度。完整代码:

minDiff :: [Int] -> Int
minDiff h = minimum (zipWith (-) (drop 1 h) h)

请注意,这会在空列表中崩溃。另一方面,不需要9999999黑客。由于懒惰,它也在恒定的空间中运行。

为了更好的错误处理:

minDiff :: [Int] -> Int
minDiff [] = error "minDiff: empty list"
minDiff h = minimum (zipWith (-) (drop 1 h) h)

甚至更迂腐(但尊重整体):

minDiff :: [Int] -> Maybe Int
minDiff [] = Nothing
minDiff h  = Just (minimum (zipWith (-) (drop 1 h) h))