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