在Haskell中编写以下程序的更好方法

时间:2011-06-25 06:44:43

标签: haskell

我正在编写一个减少自由词的函数。可以将其视为以下算法:

想法是取消列表中的项目,如果它们彼此相反并且彼此相邻。反复应用,直到无法取消。例如 [-2,1,-1,2,3] - > [-2,2,3-] - GT; [3]

我写了以下代码。它看起来并不优雅。它使用头部,尾部多次,并且这个功能的输入总共有3种模式,如果它可以减少到2则很好。 我想知道是否有更优雅的方法在Haskell中编写它。我怀疑我可以使用折叠,但我不知道如何自然地做到这一点。

freeReduce []  = []
freeReduce [x] = [x]
freeReduce (x:xs)
  | x == -(head xs) = freeReduce (tail xs)
  | otherwise       = if' (rest == [])
                          [x]
                          (if' (x == - (head rest)) (tail rest) (x:rest))
  where rest = freeReduce xs

7 个答案:

答案 0 :(得分:14)

这是我能做到的最清楚的事情:

freeReduce []       = []
freeReduce (x : xs) = case freeReduce xs of
                           y : ys | y == -x ->     ys
                           ys               -> x : ys

或等效地:

freeReduce = foldr f []
  where f x (y : ys) | y == -x =     ys
        f x ys                 = x : ys

(两者均未经测试。)

似乎freeReduce本质上是严格的。

(我原来的,不正确的尝试:

freeReduce (x : y : rest) | x == -y =     freeReduce rest
freeReduce (x : rest)               = x : freeReduce rest
freeReduce []                       =     []

(未测试))

答案 1 :(得分:12)

您需要访问当前检查点之前和之后的元素,如下所示:

freeReduce :: (Num a) => [a] -> [a]
freeReduce = red []
  where red xs         []           = reverse xs
        red (x:xs) (y:ys) | x == -y = red    xs  ys
        red xs     (y:ys)           = red (y:xs) ys

您将元素从第二个列表移动到第一个列表,并且只比较这些列表的顶部。所以这是清单上的一个扫描,然后在最后将其反转。

答案 2 :(得分:6)

以下代码不足够吗?

freeReduce[] = []
freeReduce(x:xs) 
  | rest == []         = [x]
  | x == -(head rest)  = (tail rest)
  | otherwise          = (x:rest)
  where rest = freeReduce xs

理念是rest总是尽可能减少,因此唯一可以改善的方法是,x之前的rest取消rest的头部1}}留下rest的尾部作为结果。

编辑:添加了一行来处理空rest

答案 3 :(得分:5)

你可以将它分成两个独立的函数,一个只检查列表的前两个元素是否相互抵消,另一个用它来减少整个列表。

-- check if the first two elements cancel each other
headReduce (x:y:zs) | x == -y = zs
headReduce xs = xs

-- build a whole reduced list from that
freeReduce []     = []
freeReduce (x:xs) = headReduce (x : freeReduce xs)

它的作用是因为如果列表完全缩小并且您在前面添加了另一个元素,那么唯一可能的新减少是前两个元素现在相互抵消。然后,每次归纳,freeReduce的结果总是完全减少。

答案 4 :(得分:2)

这是一个班轮,我希望它确实涵盖了所有案例,因为我没有对它进行过多次测试

freeReduce = foldr (\i a -> if a /= [] && i == -(hea­d a) then tail a else i:a ) []

答案 5 :(得分:1)

这看起来像是家庭作业,所以我只打算提示。

您需要比较列表中的前两项,但也允许只包含一个元素或没有元素的列表,因此您的案例如下所示:

  freeReduce (x1 : x2 : xs) = ....
  freeReduce [x] = [x]
  freeReduce [] = []

涵盖了所有案例。所以现在你只需要决定如何处理相邻的项目x1和x2,以及如何将其提供给其余的计算。

答案 6 :(得分:1)

在后面的列表中收集已经检查过的元素,并在找到匹配时“退一步”:

freeReduce xs = reduce [] xs where
   reduce acc [] = reverse acc
   reduce [] (x:y:ys) | x == -y = reduce [] ys 
   reduce (a:as) (x:y:ys) | x == -y = reduce as (a:ys) 
   reduce acc (x:xs) = reduce (x:acc) xs