我试图创建一个列入一个列表的函数,如果其中一个元素是负数,那么该列表中与其正对应元素相等的任何元素都应该更改为0.例如,如果列表中有-2,那么该列表中的所有2都应该更改为0.
任何想法为什么它只适用于某些情况而不适用于其他情况?我不明白为什么会这样,我已经多次看过了。
changeToZero [] = []
changeToZero [x] = [x]
changeToZero (x:zs:y:ws) | (x < 0) && ((-1)*(x) == y) = x : zs : 0 : changeToZero ws
changeToZero (x:xs) = x : changeToZero xs
*主&GT; changeToZero [-1,1,-2,2,-3,3]
[ - 1,1,-2,2,-3,3]
*主&GT; changeToZero [-2,1,2,3]
[ - 2,1,0,3]
*主&GT; changeToZero [-2,1,2,3,2]
[ - 2,1,0,3,2]
*主&GT; changeToZero [1,-2,2,2,1]
[1,-2,2,0,1]
答案 0 :(得分:6)
我认为列表理解更清晰,更容易在这里找到。
changeToZero xs = [if x > 0 && (-x) `elem` xs then 0 else x | x <- xs]
如果您需要更高效的内容,可以构建一组否定元素并检查它而不是使用elem
。
import qualified Data.Set as Set
changeToZero' xs = [if (-x) `Set.member` unwanted then 0 else x | x <- xs]
where unwanted = Set.fromList $ filter (< 0) xs
答案 1 :(得分:1)
你实际上并不记得你在列表中找到的负面符号
import qualified Data.Set as S
changeToZero :: [Int] -> [Int]
changeToZero [] = []
changeToZero xs = reverse . snd $ foldl f (S.empty,[]) xs
where
f (negs,res) x | x < 0 = (S.insert (-x) negs, x:res)
| S.member x negs = (negs,0:res)
| otherwise = (negs,x:res)
答案 2 :(得分:1)
那么,在@jdevelop的答案的基础上,如果负数必须出现在正数之前才能计算,那么你可以通过输入一次传递来构建结果,而不需要反转它:
import qualified Data.Set as S
import Control.Monad.State
changeToZero :: [Int] -> [Int]
changeToZero xs = evalState (mapM f xs) S.empty where
f x | x < 0 = modify (S.insert (-x)) >> return x
| otherwise = gets (S.member x) >>= \hasNeg -> return $ if hasNeg then 0 else x
通过这种方式,您可以得到答案
take 4 $ changeToZero $ 1 : (-2) : 3 : 2 : undefined
其他解决方案将失败。
**编辑**
这是同样的事情,但没有State
monad,这使它更容易理解:
changeToZero' :: [Int] -> [Int]
changeToZero' = go S.empty where
go _ [] = []
go s (x:xs) | x < 0 = x : go (S.insert (-x) s) xs
| S.member x s = 0 : go s xs
| otherwise = x : go s xs