从列表中选择特定图片

Pick a specific picture from a list

我有以下功能:

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

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

编辑: 我创建了以下函数,但由于某种原因,我收到图片错误 "Variable not in scope"。

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"
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"

I'm getting the error "Variable not in scope" to the pictures.

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

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

所以你在这里可以做的是return一个Maybe Picture,它的值是NothingJust pic,这取决于你是否可以return一张图片或不是。或者您可以使用 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 到 return 关联的 xn(或 Nothing)。

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

This returns Nothing 虽然没有找到。但如果您愿意,可以通过 maybe :: b -> (a -> b) -> Maybe a -> b.

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

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

您不能只丢弃未加载的图片;如果您尝试加载 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,我们有 lookup returns Nothing 如果 n 太大的场景; Just Nothing 如果所需图片加载失败,Just (Just pic) 如果找到所需图片。 Control.Monad 中的 join 函数将 lookup returns 的 Maybe (Maybe Picture) 值减少为我们想要的 "regular" Maybe Picture