【问题标题】:Pick a specific picture from a list从列表中选择特定图片
【发布时间】:2019-05-10 01:00:13
【问题描述】:

我有以下功能:

blockToPicture :: Int -> [Picture] -> Picture
blockToPicture n [pic1,pic2,pic3] | n==0 = ...
                                  | n==1 = ...
                                  | otherwise = ...

如果n==0 我想选择pic1,如果n==1 我想选择pic2。否则我想选择pic3。问题是当其中一张图片没有加载时,它不会出现在列表中。 而不是[pic1,pic2,pic3] 我有类似[Pic1,Pic3] 的东西。 当函数是supposed 来选择不在列表中的图片时,我希望它改为写"X"。为此,我将使用该功能 text "X" 代替。问题是我不知道如何让它写"X"而不是选择错误的图片。

编辑: 我创建了以下函数,但由于某种原因,我在图片中收到错误“变量不在范围内”。

blocoParaPicture :: Int -> [Picture] -> Picture
blocoParaPicture b l | b==0 = if elem pic1 l then pic1 else text "X"
                     | b==1 = if elem pic2 l then pic2 else text "X"
                     | otherwise = if elem pic3 l then pic3 else text "X"

【问题讨论】:

  • 好吧,您可以再次对 [pic1, pic2][pic1][] 等列表进行模式匹配,并且每次都替换不再涵盖的案例。但我建议您实现一种递归模式,在递减索引和列表尾部进行递归。
  • 不不不,你应该使用[Maybe Picture]类型的列表。
  • 文本“X”函数有什么作用?可以使用 (Either String Picture) 代替。

标签: haskell gloss juicy-pixels


【解决方案1】:

您不能只丢弃未加载的图片;如果您尝试加载 3 张图片并以 [some_pic, some_other_pic] 结尾,您怎么知道哪张没有加载?您需要[Maybe Picture] 类型的列表,其中Just pic 表示成功加载的图片,Nothing 表示失败。然后你的函数看起来像

blockToPicture :: Int -> [Maybe Picture] -> Maybe Picture
blockToPicture _ []          = Nothing                  -- No pictures to choose from
blockToPicture 0 (Nothing:_) = Nothing                  -- Desired picture failed to load
blockToPicutre 0 (x:_)       = x                        -- Found desired picture!
blockToPicture n (_:xs)      = blockToPicture (n-1) xs  -- This isn't it; try the next one

改编 Jorge Adriano 的建议以使用 lookup(这是一个很好的建议)

import Control.Monad

blockToPicture :: Int -> [Maybe Picture] -> Maybe Picture
blockToPicture n pics = join (lookup n (zip [0..] pics))

因为lookup :: a -> [(a,b)] -> Maybe bb 这里是Maybe Picture,所以我们有 如果n 太大,lookup 返回Nothing 的场景; Just Nothing 如果所需图片无法加载,Just (Just pic) 如果找到所需图片。来自Control.Monadjoin 函数将lookup 返回的Maybe (Maybe Picture) 值减少为我们想要的“常规”Maybe Picture

【讨论】:

    【解决方案2】:
    blocoParaPicture :: Int -> [Picture] -> Picture
    blocoParaPicture b l | b==0 = if elem pic1 l then pic1 else text "X"
                         | b==1 = if elem pic2 l then pic2 else text "X"
                         | otherwise = if elem pic3 l then pic3 else text "X"
    

    图片出现“变量不在范围内”错误。

    表达式elem x xs 检查给定的x 是否在列表xs 中。在您编写pic1 时的代码中,范围内没有这样的变量,它没有在任何地方定义。在任何情况下,您都不想在列表中搜索特定值,而是想知道给定位置是否“存在”,即列表是否足够长。

    您也不能只在这种类型的函数中“写入”。在 Haskell 中,输入和输出体现在类型上。这是一个纯函数,它接受一些参数并计算结果,没有副作用。

    所以你可以在这里做的是返回一个Maybe Picture,它的值是NothingJust pic,这取决于你是否可以返回图片。或者您可以使用Either String Picture,其中值的格式为Left stringRight pic。让我们选择后一种选择。

    blocoParaPicture :: Int -> [Picture] -> Either String Picture
    

    在实施方面,我们可以偏离主题,讨论错误管理(因为问题是访问某个职位可能会失败)。但在这一点上,我认为最好避免走弯路,所以让我们保持(相对)简单。

    直接递归(最简单)

    最简单最直接的方法是直接递归(正如@chepner 在下面的 cmets 中所建议的那样)。

    blocoParaPicture :: Int -> [Picture] -> Either String Picture
    blocoParaPicture _ []     = Left "X"
    blocoParaPicture 0 (x:_)  = Right x
    blocoParaPicture n (x:xs) = safe (n-1) xs
    

    确保!!成功

    如果您确实想使用标准访问函数!!,一种解决方法(但在一般情况下可能效率低下)是构造一个“安全”无限列表。

    import Data.List 
    
    blocoParaPicture :: Int -> [Picture] -> Either String Picture
    blocoParaPicture n xs = zs !! n 
                            where zs = [Right x | x <- xs] ++ repeat (Left "X")
    

    列表zs 是由两个列表组成的无限列表。首先 [Right x | x &lt;- xs] 就像您的原始列表一样,但每个元素 x 变为 Right x。然后从那时起,所有元素都采用Left "X" 的形式来指示失败。一般来说,上述方法可能效率低下。如果您在列表中寻找大的n

    [Right 1, Right 2] ++ [Left "X", Left "X", ...
    

    您正在执行许多不必要的步骤,因为您可能会在第一个列表结束时停止。但是对于小的 n 来说效果很好。

    使用lookup

    与您尝试使用elem 函数类似的另一种可能性是在索引上使用lookup。这个函数在设计上是安全的。

    lookup :: Eq a => a -> [(a, b)] -> Maybe b
    

    按照这种方法,您首先构建列表,

    [(0,x0), (1,x1), (2,x2) ...(k,xk)]
    

    然后查找给定的n 以返回关联的xn(或Nothing)。

    blocoParaPicture' :: Int -> [Picture] -> Maybe Picture
    blocoParaPicture' n xs = lookup n (zip [1..] xs)
    

    这会在未找到时返回 Nothing。但如果您愿意,您可以通过maybe :: b -&gt; (a -&gt; b) -&gt; Maybe a -&gt; b 转换为Either

    blocoParaPicture :: Int -> [Picture] -> Either String Picture
    blocoParaPicture n xs = maybe (Left "X") Right (lookup n (zip [1..] xs))
    

    当您只需要一个简单的访问函数时,这肯定有点太复杂了。但在事情不那么简单的情况下可以派上用场。

    【讨论】:

    • nlength xs方式 的病态情况下,只检查n &gt; length xs 比迭代足够大的无限列表。
    • 或者直接写递归而不是使用!!:bPP _ [] = Left "X"; bPP 0 (x:_) = Right x; bPP n (x:xs) = bPP (n-1) xs
    • 确实你是对的,我考虑过。在任何情况下,他都是一个初学者,据说只看一个只有 3 个元素的列表……每种方法都教会了他一些东西。
    • 添加了您的建议,也许这是最好的。试图坚持使用!! 可能会使它变得不必要地复杂。我之前还查找了!! 的“安全”替代方案,但只找到了Data.List.Safe,但它使用与标准函数相同的名称,这在这里会造成混淆。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-22
    • 2014-10-03
    • 2019-06-06
    • 2016-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多