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