从列表中选择特定图片

时间:2018-12-08 14:22:09

标签: haskell gloss juicy-pixels

我具有以下功能:

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"

2 个答案:

答案 0 :(得分: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 bbMaybe Picture,因此我们有 如果lookup过大,则Nothing返回n的情况; Just Nothing(如果所需图片无法加载),Just (Just pic)(如果找到所需图片)。 join中的Control.Monad函数将Maybe (Maybe Picture)返回到我们想要的“常规” lookup的{​​{1}}值减小。

答案 1 :(得分:0)

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在下面的评论中所建议)。

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 <- 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。但是,如果愿意,您可以通过Either转换为maybe :: b -> (a -> b) -> Maybe a -> b

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

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