Выберите конкретную картинку из списка

У меня есть следующая функция:

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))

Это, конечно, слишком сложно, когда все, что вам нужно, это простая функция доступа. Но может пригодиться в ситуациях, когда все не так просто.

Другие вопросы по тегам