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