0

我有以下功能:

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

2 回答 2

1

您不能只丢弃未加载的图片;如果您尝试加载 3 张图片并最终得到[some_pic, some_other_pic],您怎么知道哪张没有加载?您需要一个 type 列表[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

改编豪尔赫阿德里亚诺的建议使用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返回太大;如果所需的图片无法加载,以及是否找到所需的图片。函数 from减少返回到我们想要的“常规”的值。NothingnJust NothingJust (Just pic)joinControl.MonadMaybe (Maybe Picture)lookupMaybe Picture

于 2018-12-08T17:19:05.547 回答
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 中,输入和输出体现在类型上。这是一个纯函数,它接受一些参数并计算结果,没有副作用。

所以你可以在这里做的是返回 a Maybe Picture,它有值Nothing或者Just 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会在找不到时返回。但如果您愿意,您可以转换为 Eithervia 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))

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

于 2018-12-08T15:17:12.350 回答