我具有以下功能:
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"
答案 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 b
和b
是Maybe 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
,其值取决于您是否可以返回图片,其值为Nothing
或Just pic
。或者,您可以使用Either String Picture
,其中值的格式为Left string
或Right 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))
当您只需要一个简单的访问功能时,这肯定有点复杂。但是在事情不那么简单的情况下可以派上用场。