Итерация по списку списков из 3-х кортежей в Haskell - PullRequest
0 голосов
/ 12 октября 2018

plusOne :: [[(Int, Int, Int)]] -> [[(Int, Int, Int)]]

Дан список списков из 3-х кортежей.Если я хочу перебрать список и +1 к значениям Int, как мне подойти к этому?Я не уверен, должен ли это быть сценарий, в котором должны использоваться Карты.

Может ли кто-нибудь указать мне правильное направление?

Ответы [ 3 ]

0 голосов
/ 12 октября 2018

Вот пример не очень общего способа, которым мы можем использовать карту для доступа к вложенным спискам, которые также могут быть сопоставлены на 3 наборах с лямбда-функцией:

fun :: (Num a, Num b, Num c) => [[(a, b, c)]] -> [[(a, b, c)]]
fun xs = map (map(\(x,y,z) -> (x+1,y+1,z+1))) xs

Плюсы: En easy иПонятный onininer для решения конкретной проблемы

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

Отображение с жесткимзакодированная функция, заставляющая вас делать новую карту для каждой операции.Так что лучшим способом в этом случае было бы реорганизовать саму функцию, то есть:

fun2 f xs = map (map(op f)) xs
    where
    op f' (x,y,z) = (f' x,f' y, f' z)

Делая op функцией, которой вы можете дать операцию для определенного типа.

Делаем сигнатуру для функции более общей для типов операций: (Обратите внимание, что мы больше не можем быть уверены в типе x,y,z, который раньше был числовым (из-за операции +1), что дает нам более универсальный типверсия функции, но также делает нас более ответственными за правильное сопоставление типов, без строковых операций над целыми числами и т. д.)

fun2 :: (t -> c) -> [[(t, t, t)]] -> [[(c, c, c)]]
0 голосов
/ 12 октября 2018

Определите правильный функтор для обертывания ваших кортежей.

data Three a = Three {getThree :: (a, a, a)} deriving (Show, Functor)

Если вы не хотите использовать расширение DeriveFunctor, определение простое:

instance Functor Three where
  fmap f (Three (x, y, z)) = Three (f x, f y, f z)

ТогдаВы можете просто определить plusOne как

>>> plusOne = let f = getThree . fmap (+1) . Three in fmap (fmap f)

, где f - это функция, которая оборачивает 3-кортеж, отображает (+1) на каждый элемент и разворачивает результат.Это сопоставляется с вашим списком списков:

> x = [[(1, 2, 3), (4,5,6)], [(7,8,9)]]
> plusOne x
[[(2,3,4),(5,6,7)],[(8,9,10)]]

Вы также можете использовать Data.Functor.Compose, чтобы исключить один из уровней fmap (или, по крайней мере, скрыть его за другим набором именчтобы разбить монотонность):

> getCompose . fmap (getThree . fmap (+1) . Three) . Compose $ x
[[(2,3,4),(5,6,7)],[(8,9,10)]]

Мы применили один и тот же шаблон обертывания / создания / распаковки дважды.Мы можем абстрагироваться от вспомогательной функции

-- wrap, map, and unwrap
wmu pre post f = post . fmap f . pre

plusOne = wmu Compose getCompose $ wmu Three getThree $ (+1)

Можно заметить сходство между wmu и dimap (специализируется на (->)):

wmu pre post = dimap pre post . fmap

Все будет еще проще, если вы в первую очередь сможете заменить универсальный кортеж на пользовательский тип продукта.

data Triplet a = Triplet a a a

-- Can be derived as well
instance Functor Triplet where
    fmap f (Triplet x y z) = Triplet (f x) (f y) (f z)

plusOne :: [[Triplet Int]] -> [[Triplet Int]]
plusOne = fmap (fmap (fmap (+1)))
0 голосов
/ 12 октября 2018

Разделить функции.Списки просты;один map каждый.Но кортежи не пересекаются так, как в Python, поэтому им требуется распаковка для доступа к элементам;это возможно при обычном программировании, но гораздо проще при сопоставлении с образцом.Кортежи могут содержать поля разных типов, поэтому что-то вроде map не может получить доступ ко всем из них.Мы можем сделать наш собственный аналог карты для троек конкретно:

map3t :: (x -> y) -> (x, x, x) -> (y, y, y)
map3t f (a, b, c) = (f a, f b, f c)

Теперь мы можем проверять каждый уровень нашего типа и обрабатывать его отдельно:

op :: Int -> Int
op = (+1)
t3 :: (Int, Int, Int) -> (Int, Int, Int)
t3 = map3t op
lt3 :: [(Int, Int, Int)] -> [(Int, Int, Int)]
lt3 = map t3
llt3 :: [[(Int, Int, Int)]] -> [[(Int, Int, Int)]]
llt3 = map lt3

Это не рекурсия, хотяmap может быть реализовано с использованием рекурсии (Data.List.map is).Каждая функция вызывает отдельную функцию для внутреннего уровня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...