Выберите конкретную картинку из списка - PullRequest
0 голосов
/ 08 декабря 2018

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

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 ]

0 голосов
/ 08 декабря 2018

Вы не можете просто удалить картинку, которая не загружается;если вы попытались загрузить 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, которое мы хотим.

0 голосов
/ 08 декабря 2018
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))

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

...