【问题标题】:Create list of lists from a list while changing one and always different value Haskell从列表中创建列表列表,同时更改一个始终不同的值 Haskell
【发布时间】:2019-07-14 11:37:56
【问题描述】:

我有一个包含 9 个整数的列表,其值为 1、-1、0,例如:

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

我想做的是从这个列表中创建列表列表,其中每个列表只包含一个更改并且始终不同。对于每个-1,我想将其更改为0。

示例:

从列表中:

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

我想得到结果:

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

所以每个列表只有一个值改变,每个都有不同的值。我什至不知道如何开始。

【问题讨论】:

  • 像在 Haskell 中一样开始:使用类型签名。之后,尝试使用该类型签名编写最愚蠢的函数,在您不知道该做什么的地方为自己留出空白。

标签: list haskell int


【解决方案1】:

显然这是一个非常具体的问题。放眼大局通常很有用:这是什么更普遍的问题?显然,在这里,我们正在查看一个列表,并且可能会以零种或多种方式看到我们希望替换的元素。此外,我们希望看看有多少种方法可以进行有限数量的此类替换。因此,让我们先实现一般情况,然后再考虑如何专门解决我们的原始问题:

import Control.Applicative (Alternative, empty, (<|>))

replaceNTimes :: Alternative f => (a -> f a) -> Int -> [a] -> f [a]
replaceNTimes _ 0 xs = pure xs
replaceNTimes _ _ [] = empty
replaceNTimes f n (x:xs) = replaceHere <|> keepLooking
  where replaceHere = (:) <$> f x <*> replaceNTimes f (n - 1) xs
        keepLooking = (x:) <$> replaceNTimes f n xs

如果我们有零替换的“预算”,我们只需返回列表的其余部分。如果我们有剩余预算但列表为空,我们会中止,因为我们未能进行预期的替换次数。否则,我们会参考我们的替换建议函数来查看哪些替换在当前位置是合法的,然后选择生成其中一个并使用较小的 N 进行递归,或者不生成并使用相同的 N 进行递归。

有了这个工具,最初的问题很简单:我们只是将 N 特化为 1(只替换一次),并提供一个替换函数,只建议用 0 替换 -1:

replaceSingleNegativeOneWithZero :: [Int] -> [[Int]]
replaceSingleNegativeOneWithZero = replaceNTimes go 1
  where go (-1) = [0]
        go _ = []

并进行测试以确保我们得到预期的输出:

*Main> replaceSingleNegativeOneWithZero [-1,0,0,1,-1,-1,1,1,0]
[ [0,0,0,1,-1,-1,1,1,0]
, [-1,0,0,1,0,-1,1,1,0]
, [-1,0,0,1,-1,0,1,1,0]]

【讨论】:

    【解决方案2】:

    再试一次:

    zeros :: [Int] -> [Int] -> [[Int]]
    zeros _ []     = []
    zeros h (x:xs) = [h ++ newX:xs] ++ zeros nextH xs
        where newX = if x == (-1) then 0 else x
            nextH = h ++ [x]
    
    switch xs = ((filter (/= xs)) . (zeros [])) xs
    

    用法:

    main = print $ switch [-1, 0, 0, 1, -1, -1, 1, 1, 0]
    

    【讨论】:

    • 我相信你不需要 nub 如果你已经检查过它们与原来的不同
    • @Lorenzo。谢谢你。你说的对!我会改变的
    【解决方案3】:

    您始终需要的第一件事就是找出函数的类型签名。在你的情况下,你想要

    lister :: [Int] -> [[Int]]
    

    然后,当您想要遍历列表但要跟踪已更改的索引时,一种简单的方法是列出您的列表列表(很难理解,只需查看代码)然后压缩它用它的索引。然后为每个列表切换该位置的元素。这是你的代码

    lister :: [Int] -> [[Int]]
    lister ls = [switch i l | (i,l) <- zip [0..9] (repeat ls)]
    

    然后你需要一个 switch 函数,根据你的规则切换第 i 个位置的元素:

    switch :: Int -> [Int] -> [Int]
    switch 0 ls = ls
    switch n ls = [if i == n && x == -1 then 0 else x | (i,x) <- zip [1..] ls]
    

    请注意,这会返回 9 个列表,每个列表对应原始列表中的每个元素。因此它包含一些重复项。您可以使用Data.List 中的nub 消除它们,注意因为它是O(n^2)

    这是你的完整代码:

    import Data.List
    
    lister :: [Int] -> [[Int]]
    lister ls = nub [switch i l | (i,l) <- zip [0..9] (repeat ls)]
    
    switch :: Int -> [Int] -> [Int]
    switch 0 ls = ls
    switch n ls = [if i == n && x == -1 then 0 else x | (i,x) <- zip [1..] ls]
    

    【讨论】:

    • @MichaelLitchard 我认为这是一个糟糕的问题,但你无法在一个问题之后真正猜出某人是否是“帮助吸血鬼”。这就是为什么我选择以正确的方式编写自己的答案,并希望其他可能会看到这个问题的观众感兴趣,但作为复制/粘贴作业解决方案显然无用。
    • @amalloy 我认为我的评论措辞不够好。我更关心并指的是整个网站。想象一下,如果我们放纵每一个显然是在寻找其他人来完成工作的懒惰问题,我们会变成什么样子?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2020-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多