Выберите конкретную картинку из списка
У меня есть следующая функция:
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"
2 ответа
Вы не можете просто отказаться от картинки, которая не загружается; если вы попытались загрузить 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
Адаптация предложения Хорхе Адриано для использования 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
возвращается в "обычный" Maybe Picture
что мы хотим.
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
убедиться !!
succeds
Если вы хотите использовать стандартную функцию доступа !!
Один из способов сделать это (но в общем случае потенциально неэффективный) - создать "безопасный" бесконечный список.
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))
Это, конечно, слишком сложно, когда все, что вам нужно, это простая функция доступа. Но может пригодиться в ситуациях, когда все не так просто.