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))
Это, безусловно, слишком сложно, когда все, что вам нужно, - это простая функция доступа.Но может пригодиться в ситуациях, когда все не так просто.