Zip со значением по умолчанию вместо сброса значений?
Я ищу функцию в haskell, чтобы закрепить два списка, которые могут различаться по длине.
Все функции zip, которые я мог найти, просто отбрасывают все значения списков, которые больше, чем другие.
Например:
В моем упражнении у меня есть два примера.
Если первый из них короче второго, я должен заполнить с помощью 0. В противном случае я должен использовать 1.
Мне не разрешено использовать рекурсию. Мне просто нужно использовать функции более высокого порядка.
Есть ли какая-либо функция, которую я могу использовать?
Я до сих пор не нашел решения.
Ответы
Ответ 1
Вы можете добавить список inifinte из 0 или 1 в каждый список, а затем взять номер, который вам нужен, из результирующего списка:
zipWithDefault :: a -> b -> [a] -> [b] -> [(a,b)]
zipWithDefault da db la lb = let len = max (length la) (length lb)
la' = la ++ (repeat da)
lb' = lb ++ (repeat db)
in take len $ zip la' lb'
Ответ 2
Существует определенная структура этой проблемы, и здесь она приходит. Я буду использовать этот материал:
import Control.Applicative
import Data.Traversable
import Data.List
Прежде всего, списки-с-дополнением являются полезной концепцией, поэтому пусть у них есть тип.
data Padme m = (:-) {padded :: [m], padder :: m} deriving (Show, Eq)
Далее, я помню, что операция truncating- zip
вызывает экземпляр Applicative
в библиотеке как newtype ZipList
(популярный пример не Monad
). Applicative ZipList
означает украшение моноида, заданное бесконечностью и минимумом. Padme
имеет аналогичную структуру, за исключением того, что его основной моноид - это положительные числа (с бесконечностью), используя один и максимум.
instance Applicative Padme where
pure = ([] :-)
(fs :- f) <*> (ss :- s) = zapp fs ss :- f s where
zapp [] ss = map f ss
zapp fs [] = map ($ s) fs
zapp (f : fs) (s : ss) = f s : zapp fs ss
Я обязан произнести обычное заклинание для создания экземпляра Functor
по умолчанию.
instance Functor Padme where fmap = (<*>) . pure
Таким образом, мы можем убрать прочь! Например, функция, которая берет оборванный список строк и заполняет их пробелами, становится одним лайнером.
deggar :: [String] -> [String]
deggar = transpose . padded . traverse (:- ' ')
См?
*Padme> deggar ["om", "mane", "padme", "hum"]
["om ","mane ","padme","hum "]
Ответ 3
Это можно выразить с помощью These
("представляет значения с двумя неисключительными возможностями") и Align
("функторы, поддерживающие операцию zip, которая принимает объединение неоднородных фигур") из библиотеки эти:
import Data.Align
import Data.These
zipWithDefault :: Align f => a -> b -> f a -> f b -> f (a, b)
zipWithDefault da db = alignWith (fromThese da db)
salign
и другие специализированные выравнивания в Data.Align
также стоит посмотреть.
Благодаря u/WarDaft, u/gallais и u/sjakobi из r/haskell за указание на этот ответ должны существовать здесь.
Ответ 4
Это должно сделать трюк:
import Data.Maybe (fromMaybe)
myZip dx dy xl yl =
map (\(x,y) -> (fromMaybe dx x, fromMaybe dy y)) $
takeWhile (/= (Nothing, Nothing)) $
zip ((map Just xl) ++ (repeat Nothing)) ((map Just yl) ++ (repeat Nothing))
main = print $ myZip 0 1 [1..10] [42,43,44]
В принципе, добавьте бесконечный список Nothing
в конец обоих списков, затем застегните их и отбросьте результаты, когда оба параметра Nothing
. Затем замените Nothing
на соответствующее значение по умолчанию, отбросив ненужный Just
, пока вы на нем.
Ответ 5
Вам не нужно сравнивать длины списков. Попытайтесь подумать о своей zip-функции как функции, принимающей только один аргумент xs
и возвращая функцию, которая примет ys
и выполнит требуемый zip. Затем попробуйте написать рекурсивную функцию, которая повторяется только на xs
, как показано ниже.
type Result = [Int] -> [(Int,Int)]
myZip :: [Int] -> Result
myZip [] = map (\y -> (0,y)) -- :: Result
myZip (x:xs) = f x (myZip xs) -- :: Result
where f x k = ??? -- :: Result
Как только вы найдете f, обратите внимание, что вы можете перевернуть рекурсию выше в складку!
Ответ 6
Как вы сказали, стандартный zip :: [a] -> [b] -> [(a, b)]
отбрасывает элементы из более длинного списка. Чтобы внести поправки в этот факт, вы можете изменить свой ввод, прежде чем передавать его zip
. Сначала вам нужно будет выяснить, какой список является более коротким (скорее всего, используя length
). Например.
zip' x xs y ys | length xs <= length ys = ...
| otherwise = ...
где x
- значение по умолчанию для более коротких xs
и y
значения по умолчанию для более короткого ys
.
Затем вы расширяете более короткий список с требуемыми элементами по умолчанию (достаточно для учета дополнительных элементов другого списка). Для этого аккуратный трюк без необходимости знать длину более длинного списка - это использовать функцию repeat :: a -> [a]
, которая бесконечно повторяет свой аргумент.
zip' x xs y ys | length xs <= length ys = zip {-do something with xs-} ys
| otherwise = zip xs {-do something with ys-}
Ответ 7
Вот еще одно решение, которое работает в бесконечных списках и представляет собой прямое обновление функций прелюдии zip:
zipDefault :: a -> b -> [a] -> [b] -> [(a,b)]
zipDefault _da _db [] [] = []
zipDefault da db (a:as) [] = (a,db) : zipDefault da db as []
zipDefault da db [] (b:bs) = (da,b) : zipDefault da db [] bs
zipDefault da db (a:as) (b:bs) = (a,b) : zipDefault da db as bs
и
zipDefaultWith :: a -> b -> (a->b->c) -> [a] -> [b] -> [c]
zipDefaultWith _da _db _f [] [] = []
zipDefaultWith da db f (a:as) [] = f a db : zipDefaultWith da db f as []
zipDefaultWith da db f [] (b:bs) = f da b : zipDefaultWith da db f [] bs
zipDefaultWith da db f (a:as) (b:bs) = f a b : zipDefaultWith da db f as bs
@pigworker, спасибо за ваше просветительское решение!
Ответ 8
Нет length
, нет счета, нет рекурсий, сделанных вручную, нет сотрудничающих сгибов. transpose
добивается цели:
zipLongest :: a -> b -> [a] -> [b] -> [(a,b)]
zipLongest x y xs ys = map head . transpose $ -- longest length;
[ -- view from above:
zip xs
(ys ++ repeat y) -- with length of xs
, zip (xs ++ repeat x)
ys -- with length of ys
]
Результат transpose
такой же длинный, как и самый длинный в его входном списке списков. map head
берет первый элемент в каждом "столбце", то есть ту пару, которая нам нужна, какой бы ни был самый длинный список.
(обновление :) Для произвольного числа списков эффективное заполнение до максимальной длины - с целью избежать потенциально квадратичного поведения других последовательно комбинируемых подходов - может следовать той же идее:
padAll :: a -> [[a]] -> [[a]]
padAll x xss = transpose $
zipWith const
(transpose [xs ++ repeat x | xs <- xss]) -- pad all, and cut
(takeWhile id . map or . transpose $ -- to the longest list
[ (True <$ xs) ++ repeat False | xs <- xss])
> mapM_ print $ padAll '-' ["ommmmmmm", "ommmmmm", "ommmmm", "ommmm", "ommm",
"omm", "om", "o"]
"ommmmmmm"
"ommmmmm-"
"ommmmm--"
"ommmm---"
"ommm----"
"omm-----"
"om------"
"o-------"
Ответ 9
Еще одна реализация:
zipWithDefault :: a -> b -> (a -> b -> c) -> [a] -> [b] -> [c]
zipWithDefault dx _ f [] ys = zipWith f (repeat dx) ys
zipWithDefault _ dy f xs [] = zipWith f xs (repeat dy)
zipWithDefault dx dy f (x:xs) (y:ys) = f x y : zipWithDefault dx dy f xs ys
А также:
zipDefault :: a -> b -> [a] -> [b] -> [c]
zipDefault dx dy = zipWithDefault dx dy (,)