1

我想要一个在列表列表上更改(1 到 0)的函数,当该行/列中 1 的数量不均匀时。我已经完成了这些功能:

1)查看列表中的行是否偶数:

  parityLine :: [[Int]] -> [Bool]
  parityLine [] =[]
  parityLine (x:xs)
                |sum(x) `mod` 2 == 0 = True:(parityLine(xs))
                |otherwise = False:(parityLine(xs))

2) 对列表列表中的相应元素求和:

 sumPositions :: [[Int]] -> [Int]
 sumPositions [] = []
 sumPositions (x:xs) = foldl (zipWith (+)) (repeat 0) (x:xs)

3)查看列表中的列是否偶数:

 parityColumn :: [Int] -> [Bool]
 parityColumn [] = []
 parityColumn (x:xs)
    |head(x:xs) `mod` 2 == 0 = True:parityColumn(xs)
    |otherwise = False:parityColumn(xs)

4)是否操作或使用两个布尔列表:

 bol :: [Bool] -> [Bool] -> [[Bool]]
 bol  [] _ = []
 bol (x:xs) (y:ys)= (map (||x) (y:ys)):(bol xs (y:ys))

5) 正确列表:

 correct :: [[Int]] -> [[Bool]]
 correct [] = []
 correct (x:xs)=(bol(parityLine (x:xs))(parityColumn(sumPositions(x:xs)))) 

所以我想要的是将函数更改correct为 [[Int]]->[[Int]] 执行此操作:

    My Int list(x:xs)                 With my correct function applied

    [[0,0,1,1],                            [[True,True,True,True],
     [1,0,1,1],                             [True,True,False,True],
     [0,1,0,1],                             [True,True,True,True]
     [1,1,1,1]]                             [True,True,True,True]]

现在我可以在第二行第三列中看到,False,所以我必须更正那个数字 1 以使多个 1 是偶数。如果该列表中有多个 False,我只想更正这些 1 中的一个。

结果,我希望该函数correct返回:

 [[0,0,1,1],
  [1,0,0,1],
  [0,1,0,1],
  [1,1,1,1]]

谢谢。

4

2 回答 2

2

我会给出一个从你所在的地方开始而不是从头开始的答案,所以我们做的更多是你的方式而不是我的方式。

首先让我们为单个元素执行此操作:

leaveIf :: Bool -> Int -> Int
leaveIf yes 0 = if yes then 0 else 1
leaveIf yes 1 = if yes then 1 else 0

(您可以为此使用警卫,但我的手机没有竖线字符!)

接下来我们可以为列表列表执行此操作:

edit :: [[Bool]] -> [[Int]] -> [[Int]]
edit boolss intss = zipWith (zipWith leaveIf) boolss intss

编辑:您只想更改一个,因此我们需要一种将后续Falses 变为Trues 的方法:

makeTrue :: [Bool] -> [Bool]
makeTrue xs = map (const True) xs

我用过这个功能const :: a -> b -> a。例如,const 5 'c'只是5. 我可以将该定义缩短为makeTrue = map (const True). 一旦你习惯了这样的想法,你会发现更短的版本更清晰。

oneFalse :: [[Bool]] -> [[Bool]]
oneFalse [] = []
oneFalse (xs:xss) = let (trues,falses) = break (==False) xs in
     case falses of 
       [] -> trues:oneFalse xss
       (False:others) -> (trues ++ False : makeTrue others) : map makeTrue xss

(==False)可以更简单地写成not,但可能不太清楚。所以例如

> oneFalse [[True,True,True,True],[True,False,True,False],[True,False,False,True]]
[[True,True,True,True],[True,False,True,True],[True,True,True,True]]

所以现在我们可以拥有

editOne :: [[Bool]] -> [[Int]] -> [[Int]]
editOne boolss intss = zipWith (zipWith leaveIf) (oneFalse boolss) intss
于 2012-12-08T11:11:33.750 回答
2

AndrewC 已经给出了一个解决方案,它改变了所有 1对应于Falses 的 s。如果我们只想更正第一个,我们必须找到一个替代品zipWith

leaveIf ok x = if ok then x else 1 -x

-- Varianto of zipWith, which changes at most one element of the list
modFirst :: Eq b => (a -> b -> b) -> [a] -> [b] -> [b]
modFirst _ [] _ = []
modFirst _ _ [] = []
modFirst f (x:xs) (y:ys) = z : if y == z then modFirst f xs ys else ys
  where z = f x y

edit :: [[Bool]] -> [[Int]] -> [[Int]]
edit boolss intss = modFirst (modFirst leaveIf) boolss intss

correct' :: [[Int]] -> [[Int]]
correct' xss = edit (correct' xss) xss

结果不一定是所有行/行都包含偶数个 1 的列表列表:

correct' [[0,1,0],[1,1,1],[0,1,0]] = [[1,1,0],[1,1,1],[0,1,0]

您需要对其进行多次迭代,直到所有错误都得到修复(即,计算一个固定点)。


我想补充一点,您的原始程序可以简化很多(无需更改您的算法):

parityLine :: [[Int]] -> [Bool]
parityLine = map (even . sum)

parityColumn :: [Int] -> [Bool]
parityColumn = map even

sumPositions :: [[Int]] -> [Int]
sumPositions = foldl (zipWith (+)) (repeat 0)

bol :: [Bool] -> [Bool] -> [[Bool]]
bol xs ys = map (\x -> map (||x) ys) xs

correct :: [[Int]] -> [[Bool]]
correct xs = bol (parityLine xs) (parityColumn $ sumPositions xs)
于 2012-12-08T12:22:18.347 回答