我正在编写一个减少自由词的函数。可以将其视为以下算法:
想法是取消列表中的项目,如果它们彼此相反并且彼此相邻。反复应用,直到无法取消。例如 [-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
答案 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 == -(head 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