使用foldr实现zip

发布于 2024-07-07 11:43:31 字数 392 浏览 6 评论 0原文

我目前正在阅读 Real World Haskell 的第 4 章,我正在尝试理解 根据foldr 实现foldl

(这是他们的代码:)

myFoldl :: (a -> b -> a) -> a -> [b] -> a

myFoldl f z xs = foldr step id xs z
    where step x g a = g (f a x)

我想我会尝试使用相同的技术来实现 zip ,但我似乎没有取得任何进展。 有可能吗?

I'm currently on chapter 4 of Real World Haskell, and I'm trying to wrap my head around implementing foldl in terms of foldr.

(Here's their code:)

myFoldl :: (a -> b -> a) -> a -> [b] -> a

myFoldl f z xs = foldr step id xs z
    where step x g a = g (f a x)

I thought I'd try to implement zip using the same technique, but I don't seem to be making any progress. Is it even possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

成熟稳重的好男人 2024-07-14 11:43:31
zip2 xs ys = foldr step done xs ys
  where done ys = []
        step x zipsfn []     = []
        step x zipsfn (y:ys) = (x, y) : (zipsfn ys)

这是如何工作的:(foldr 步骤完成 xs) 返回一个消耗的函数
是的; 所以我们沿着 xs 列表构建一个嵌套组合
每个函数都将应用于 ys 的相应部分。

如何想出它:我从总体想法开始(来自类似的
之前见过的例子),

zip2 xs ys = foldr step done xs ys

然后依次填写以下每一行所需的内容
是为了使类型和值正确。 这是最容易的
首先考虑最简单的情况,然后再考虑更困难的情况。

第一行可以写得更简单,如

zip2 = foldr step done

马蒂亚斯特所示。

zip2 xs ys = foldr step done xs ys
  where done ys = []
        step x zipsfn []     = []
        step x zipsfn (y:ys) = (x, y) : (zipsfn ys)

How this works: (foldr step done xs) returns a function that consumes
ys; so we go down the xs list building up a nested composition of
functions that will each be applied to the corresponding part of ys.

How to come up with it: I started with the general idea (from similar
examples seen before), wrote

zip2 xs ys = foldr step done xs ys

then filled in each of the following lines in turn with what it had to
be to make the types and values come out right. It was easiest to
consider the simplest cases first before the harder ones.

The first line could be written more simply as

zip2 = foldr step done

as mattiast showed.

青瓷清茶倾城歌 2024-07-14 11:43:31

这里已经给出了答案,但不是(说明性的)推导。 所以即使过了这么多年,也许还是值得添加它。

其实很简单。 首先,

foldr f z xs 
   = foldr f z [x1,x2,x3,...,xn] = f x1 (foldr f z [x2,x3,...,xn])
   = ... = f x1 (f x2 (f x3 (... (f xn z) ...)))

因此通过 eta 扩展,

foldr f z xs ys
   = foldr f z [x1,x2,x3,...,xn] ys = f x1 (foldr f z [x2,x3,...,xn]) ys
   = ... = f x1 (f x2 (f x3 (... (f xn z) ...))) ys

正如这里显而易见的,如果 f 在其第二个参数中是非强制的,它首先开始工作在 x1ys 上,f x1r1ys 其中 r1 =(f x2 (f x3 (... (f xn z) ...)))=折叠 fz [x2,x3,...,xn]

因此,

f x1 r1 [] = []
f x1 r1 (y1:ys1) = (x1,y1) : r1 ys1

我们通过调用 r1 来沿着列表从左到右排列信息的传递输入列表的其余部分ys1foldr fz [x2,x3,...,xn]< code>ys1 = f x2r2ys1,作为下一步。 就是这样。


ys 短于 xs(或相同长度)时,f[] 情况会触发,并且处理停止。 但是,如果 ysxs 长,那么 f[] 案例将不会触发,我们将到达最终的f xnz(yn:ysn) 应用程序,

f xn z (yn:ysn) = (xn,yn) : z ysn

因为我们已经到达了末尾xszip 处理必须停止:

z _ = []

这意味着应使用定义 z = const []

zip xs ys = foldr f (const []) xs ys
  where
    f x r []     = []
    f x r (y:ys) = (x,y) : r ys

f 的角度来看,r 扮演着成功延续的角色,f 在处理完成时调用它。发出(x,y)对后继续。

所以r“当有更多xs时,更多ys会做什么”,并且z = const []foldr 中的 nil 情况,是“使用 完成的操作ys 当不再有 xs" 时。 或者,f 可以自行停止,当 ys 耗尽时返回 []


请注意 ys 如何用作一种累加值,该值通过一次 f 调用沿列表 xs 从左向右传递到下一步(这里的“累积”步骤是从中剥离头元素)。

自然这对应于左侧折叠,其中累积步骤是“应用函数”,其中 z = id 当“不再有xs”时返回最终的累加值:

foldl f a xs =~ foldr (\x r a-> r (f a x)) id xs a

同样,对于有限列表,

foldr f a xs =~ foldl (\r x a-> r (f x a)) id xs a

由于组合函数需要决定是否继续,所以它现在可以有可以提前停止的左折叠:

foldlWhile t f a xs = foldr cons id xs a
  where 
    cons x r a = if t x then r (f a x) else a

或跳过左折叠,foldlWhen t ...

    cons x r a = if t x then r (f a x) else r a

等等。

The answer had already been given here, but not an (illustrative) derivation. So even after all these years, perhaps it's worth adding it.

It is actually quite simple. First,

foldr f z xs 
   = foldr f z [x1,x2,x3,...,xn] = f x1 (foldr f z [x2,x3,...,xn])
   = ... = f x1 (f x2 (f x3 (... (f xn z) ...)))

hence by eta-expansion,

foldr f z xs ys
   = foldr f z [x1,x2,x3,...,xn] ys = f x1 (foldr f z [x2,x3,...,xn]) ys
   = ... = f x1 (f x2 (f x3 (... (f xn z) ...))) ys

As is apparent here, if f is non-forcing in its 2nd argument, it gets to work first on x1 and ys, f x1r1ys where r1 =(f x2 (f x3 (... (f xn z) ...)))= foldr f z [x2,x3,...,xn].

So, using

f x1 r1 [] = []
f x1 r1 (y1:ys1) = (x1,y1) : r1 ys1

we arrange for passage of information left-to-right along the list, by calling r1 with the rest of the input list ys1, foldr f z [x2,x3,...,xn]ys1 = f x2r2ys1, as the next step. And that's that.


When ys is shorter than xs (or the same length), the [] case for f fires and the processing stops. But if ys is longer than xs then f's [] case won't fire and we'll get to the final f xnz(yn:ysn) application,

f xn z (yn:ysn) = (xn,yn) : z ysn

Since we've reached the end of xs, the zip processing must stop:

z _ = []

And this means the definition z = const [] should be used:

zip xs ys = foldr f (const []) xs ys
  where
    f x r []     = []
    f x r (y:ys) = (x,y) : r ys

From the standpoint of f, r plays the role of a success continuation, which f calls when the processing is to continue, after having emitted the pair (x,y).

So r is "what is done with more ys when there are more xs", and z = const [], the nil-case in foldr, is "what is done with ys when there are no more xs". Or f can stop by itself, returning [] when ys is exhausted.


Notice how ys is used as a kind of accumulating value, which is passed from left to right along the list xs, from one invocation of f to the next ("accumulating" step being, here, stripping a head element from it).

Naturally this corresponds to the left fold, where an accumulating step is "applying the function", with z = id returning the final accumulated value when "there are no more xs":

foldl f a xs =~ foldr (\x r a-> r (f a x)) id xs a

Similarly, for finite lists,

foldr f a xs =~ foldl (\r x a-> r (f x a)) id xs a

And since the combining function gets to decide whether to continue or not, it is now possible to have left fold that can stop early:

foldlWhile t f a xs = foldr cons id xs a
  where 
    cons x r a = if t x then r (f a x) else a

or a skipping left fold, foldlWhen t ..., with

    cons x r a = if t x then r (f a x) else r a

etc.

少女情怀诗 2024-07-14 11:43:31

我找到了一种与你的方法非常相似的方法:

myzip = foldr step (const []) :: [a] -> [b] -> [(a,b)]
    where step a f (b:bs) = (a,b):(f bs)
          step a f [] = []

I found a way using quite similar method to yours:

myzip = foldr step (const []) :: [a] -> [b] -> [(a,b)]
    where step a f (b:bs) = (a,b):(f bs)
          step a f [] = []
已下线请稍等 2024-07-14 11:43:31

对于这里的非本地 Haskellers,我编写了该算法的一个方案版本,以使其更清楚实际发生的情况:

> (define (zip lista listb)
    ((foldr (lambda (el func)
           (lambda (a)
             (if (empty? a)
                 empty
                 (cons (cons el (first a)) (func (rest a))))))
         (lambda (a) empty)
         lista) listb))
> (zip '(1 2 3 4) '(5 6 7 8))
(list (cons 1 5) (cons 2 6) (cons 3 7) (cons 4 8))

foldr 产生一个函数,当应用于列表时,该函数将返回列表的 zip 与提供给函数的列表折叠在一起。 由于惰性求值,Haskell 隐藏了内部 lambda 。


进一步分解:

对输入进行压缩:'(1 2 3)
Foldr 函数被调用,

el->3, func->(lambda (a) empty)

This 扩展为:

(lambda (a) (cons (cons el (first a)) (func (rest a))))
(lambda (a) (cons (cons 3 (first a)) ((lambda (a) empty) (rest a))))

如果我们现在返回 this,我们就会有一个函数,它接受一个元素的列表
并返回对(3 个元素):

> (define f (lambda (a) (cons (cons 3 (first a)) ((lambda (a) empty) (rest a)))))
> (f (list 9))
(list (cons 3 9))

继续,foldr 现在调用 func with

el->3, func->f ;using f for shorthand
(lambda (a) (cons (cons el (first a)) (func (rest a))))
(lambda (a) (cons (cons 2 (first a)) (f (rest a))))

This is a func,它接受一个包含两个元素的列表,现在,并用 (list 2 3) 压缩它们:

> (define g (lambda (a) (cons (cons 2 (first a)) (f (rest a)))))
> (g (list 9 1))
(list (cons 2 9) (cons 3 1))

发生了什么?

(lambda (a) (cons (cons 2 (first a)) (f (rest a))))

在本例中,a(list 9 1)

(cons (cons 2 (first (list 9 1))) (f (rest (list 9 1))))
(cons (cons 2 9) (f (list 1)))

并且,您还记得,f3 压缩其参数代码>.

这继续等等......

For the non-native Haskellers here, I've written a Scheme version of this algorithm to make it clearer what's actually happening:

> (define (zip lista listb)
    ((foldr (lambda (el func)
           (lambda (a)
             (if (empty? a)
                 empty
                 (cons (cons el (first a)) (func (rest a))))))
         (lambda (a) empty)
         lista) listb))
> (zip '(1 2 3 4) '(5 6 7 8))
(list (cons 1 5) (cons 2 6) (cons 3 7) (cons 4 8))

The foldr results in a function which, when applied to a list, will return the zip of the list folded over with the list given to the function. The Haskell hides the inner lambda because of lazy evaluation.


To break it down further:

Take zip on input: '(1 2 3)
The foldr func gets called with

el->3, func->(lambda (a) empty)

This expands to:

(lambda (a) (cons (cons el (first a)) (func (rest a))))
(lambda (a) (cons (cons 3 (first a)) ((lambda (a) empty) (rest a))))

If we were to return this now, we'd have a function which takes a list of one element
and returns the pair (3 element):

> (define f (lambda (a) (cons (cons 3 (first a)) ((lambda (a) empty) (rest a)))))
> (f (list 9))
(list (cons 3 9))

Continuing, foldr now calls func with

el->3, func->f ;using f for shorthand
(lambda (a) (cons (cons el (first a)) (func (rest a))))
(lambda (a) (cons (cons 2 (first a)) (f (rest a))))

This is a func which takes a list with two elements, now, and zips them with (list 2 3):

> (define g (lambda (a) (cons (cons 2 (first a)) (f (rest a)))))
> (g (list 9 1))
(list (cons 2 9) (cons 3 1))

What's happening?

(lambda (a) (cons (cons 2 (first a)) (f (rest a))))

a, in this case, is (list 9 1)

(cons (cons 2 (first (list 9 1))) (f (rest (list 9 1))))
(cons (cons 2 9) (f (list 1)))

And, as you recall, f zips its argument with 3.

And this continues etc...

晨光如昨 2024-07-14 11:43:31

所有这些 zip 解决方案的问题在于它们仅折叠一个列表或另一个列表,如果它们都是“优秀的生产者”(用列表融合的说法),这可能会成为问题。 您真正需要的是一个折叠两个列表的解决方案。 幸运的是,有一篇论文正是关于这一点的,名为“Coroutining Folds with Hyperfunctions”

您需要一个辅助类型,即超函数,它基本上是一个以另一个超函数作为参数的函数。

newtype H a b = H { invoke :: H b a -> b }

这里使用的超函数基本上就像普通函数的“堆栈”一样。

push :: (a -> b) -> H a b -> H a b
push f q = H $ \k -> f $ invoke k q

您还需要一种将两个超功能端到端地组合在一起的方法。

(.#.) :: H b c -> H a b -> H a c
f .#. g = H $ \k -> invoke f $ g .#. k

这与法律上的 push 相关:

(push f x) .#. (push g y) = push (f . g) (x .#. y)

这原来是一个关联运算符,这就是恒等式:

self :: H a a
self = H $ \k -> invoke k self

您还需要一些东西,可以忽略“堆栈”上的其他所有内容并返回特定值:

base :: b -> H a b
base b = H $ const b

最后,您需要一种从超函数中获取值的方法:

run :: H a a -> a
run q = invoke q self

run 将所有 push 函数端到端地串在一起,直到它遇到 >base 或无限循环。

因此,现在您可以使用将信息从一个列表传递到另一个列表的函数,将两个列表折叠为超函数,并组合最终值。

zip xs ys = run $ foldr (\x h -> push (first x) h) (base []) xs .#. foldr (\y h -> push (second y) h) (base Nothing) ys where
  first _ Nothing = []
  first x (Just (y, xys)) = (x, y):xys

  second y xys = Just (y, xys)

折叠两个列表很重要的原因是 GHC 所做的称为“列表融合”的事情,这在 GHC.Base 模块,但可能应该更知名。 成为一个好的列表生成器并将 buildfoldr 一起使用可以防止大量无用的生成和列表元素的立即消耗,并且可以公开进一步的优化。

The problem with all these solutions for zip is that they only fold over one list or the other, which can be a problem if both of them are "good producers", in the parlance of list fusion. What you actually need is a solution that folds over both lists. Fortunately, there is a paper about exactly that, called "Coroutining Folds with Hyperfunctions".

You need an auxiliary type, a hyperfunction, which is basically a function that takes another hyperfunction as its argument.

newtype H a b = H { invoke :: H b a -> b }

The hyperfunctions used here basically act like a "stack" of ordinary functions.

push :: (a -> b) -> H a b -> H a b
push f q = H $ \k -> f $ invoke k q

You also need a way to put two hyperfunctions together, end to end.

(.#.) :: H b c -> H a b -> H a c
f .#. g = H $ \k -> invoke f $ g .#. k

This is related to push by the law:

(push f x) .#. (push g y) = push (f . g) (x .#. y)

This turns out to be an associative operator, and this is the identity:

self :: H a a
self = H $ \k -> invoke k self

You also need something that disregards everything else on the "stack" and returns a specific value:

base :: b -> H a b
base b = H $ const b

And finally, you need a way to get a value out of a hyperfunction:

run :: H a a -> a
run q = invoke q self

run strings all of the pushed functions together, end to end, until it hits a base or loops infinitely.

So now you can fold both lists into hyperfunctions, using functions that pass information from one to the other, and assemble the final value.

zip xs ys = run $ foldr (\x h -> push (first x) h) (base []) xs .#. foldr (\y h -> push (second y) h) (base Nothing) ys where
  first _ Nothing = []
  first x (Just (y, xys)) = (x, y):xys

  second y xys = Just (y, xys)

The reason why folding over both lists matters is because of something GHC does called list fusion, which is talked about in the GHC.Base module, but probably should be much more well-known. Being a good list producer and using build with foldr can prevent lots of useless production and immediate consumption of list elements, and can expose further optimizations.

冬天旳寂寞 2024-07-14 11:43:31

我试图自己理解这个优雅的解决方案,所以我尝试自己推导类型和评估。 因此,我们需要编写一个函数:

zip xs ys = foldr step done xs ys

这里我们需要导出 stepdone,无论它们是什么。 回想一下 foldr 的类型,实例化为列表:

foldr :: (a -> state -> state) -> state -> [a] -> state

但是,我们的 foldr 调用必须实例化为如下所示的内容,因为我们必须接受的不是一个,而是两个列表参数:

foldr :: (a -> ? -> ?) -> ? -> [a] -> [b] -> [(a,b)]

因为 -> ;右关联,这相当于:

foldr :: (a -> ? -> ?) -> ? -> [a] -> ([b] -> [(a,b)])

我们的 ([b] -> [(a,b)]) 对应于原始 foldr 类型签名中的 state 类型变量,因此我们必须替换每次出现的 state< /code> :

foldr :: (a -> ([b] -> [(a,b)]) -> ([b] -> [(a,b)]))
      -> ([b] -> [(a,b)])
      -> [a]
      -> ([b] -> [(a,b)])

这意味着我们传递给 foldr 的参数必须具有以下类型:

step :: a -> ([b] -> [(a,b)]) -> [b] -> [(a,b)]
done :: [b] -> [(a,b)]
xs :: [a]
ys :: [b]

回想一下 foldr (+) 0 [1,2,3] 扩展至:

1 + (2 + (3 + 0))

因此,如果 xs = [1,2,3]ys = [4,5,6,7],我们的 foldr 调用将扩展为:

1 `step` (2 `step` (3 `step` done)) $ [4,5,6,7]

这意味着我们的 1 `step` (2 `step` (3 `step` did)) 构造必须创建一个递归函数,该函数将遍历 [4,5, 6,7] 并压缩元素。 (请记住,如果原始列表之一较长,则多余的值将被丢弃)。 IOW,我们的构造必须具有类型 [b] -> [(a,b)]

3 `step` done 是我们的基本情况,其中 done 是初始值,如 foldr (+) 0 [ 中的 0 1..3]。 我们不想压缩 3 之后的任何内容,因为 3 是 xs 的最终值,因此我们必须终止递归。 在基本情况下如何终止列表上的递归? 您返回空列表[]。 但回想一下 done 类型签名:

done :: [b] -> [(a,b)]

因此我们不能只返回 [],我们必须返回一个忽略它接收到的任何内容的函数。 因此使用 const

done = const [] -- this is equivalent to done = \_ -> []

现在让我们开始弄清楚 step 应该是什么。 它将 a 类型的值与 [b] -> 类型的函数组合在一起。 [(a,b)] 并返回 [b] -> 类型的函数 [(a,b)]

3`step`done中,我们知道稍后将进入我们的压缩列表的结果值必须是(3,6)(从原始xs得知ys)。 因此,3 `step` did 必须计算为:

\(y:ys) -> (3,y) : done ys

记住,我们必须返回一个函数,在其中我们以某种方式压缩元素,上面的代码是有意义的和类型检查的。

现在我们假设了 step 应该如何评估,让我们继续评估。 以下是我们的 foldr 评估中的所有归约步骤的样子:

3 `step` done -- becomes
(\(y:ys) -> (3,y) : done ys)
2 `step` (\(y:ys) -> (3,y) : done ys) -- becomes
(\(y:ys) -> (2,y) : (\(y:ys) -> (3,y) : done ys) ys)
1 `step` (\(y:ys) -> (2,y) : (\(y:ys) -> (3,y) : done ys) ys) -- becomes
(\(y:ys) -> (1,y) : (\(y:ys) -> (2,y) : (\(y:ys) -> (3,y) : done ys) ys) ys)

评估产生了该步骤的实现(请注意,我们通过返回一个值来提前耗尽元素。空列表):

step x f = \[] -> []
step x f = \(y:ys) -> (x,y) : f ys

因此,完整的函数 zip 实现如下:

zip :: [a] -> [b] -> [(a,b)]
zip xs ys = foldr step done xs ys
  where done = const []
        step x f [] = []
        step x f (y:ys) = (x,y) : f ys

PS:如果您受到优雅的折叠的启发,请阅读 使用foldr编写foldl,然后使用Graham Hutton的关于折叠的通用性和表现力的教程

I tried to understand this elegant solution myself, so I tried to derive the types and evaluation myself. So, we need to write a function:

zip xs ys = foldr step done xs ys

Here we need to derive step and done, whatever they are. Recall foldr's type, instantiated to lists:

foldr :: (a -> state -> state) -> state -> [a] -> state

However our foldr invocation must be instantiated to something like below, because we must accept not one, but two list arguments:

foldr :: (a -> ? -> ?) -> ? -> [a] -> [b] -> [(a,b)]

Because -> is right-associative, this is equivalent to:

foldr :: (a -> ? -> ?) -> ? -> [a] -> ([b] -> [(a,b)])

Our ([b] -> [(a,b)]) corresponds to state type variable in the original foldr type signature, therefore we must replace every occurrence of state with it:

foldr :: (a -> ([b] -> [(a,b)]) -> ([b] -> [(a,b)]))
      -> ([b] -> [(a,b)])
      -> [a]
      -> ([b] -> [(a,b)])

This means that arguments that we pass to foldr must have the following types:

step :: a -> ([b] -> [(a,b)]) -> [b] -> [(a,b)]
done :: [b] -> [(a,b)]
xs :: [a]
ys :: [b]

Recall that foldr (+) 0 [1,2,3] expands to:

1 + (2 + (3 + 0))

Therefore if xs = [1,2,3] and ys = [4,5,6,7], our foldr invocation would expand to:

1 `step` (2 `step` (3 `step` done)) $ [4,5,6,7]

This means that our 1 `step` (2 `step` (3 `step` done)) construct must create a recursive function that would go through [4,5,6,7] and zip up the elements. (Keep in mind, that if one of the original lists is longer, the excess values are thrown away). IOW, our construct must have the type [b] -> [(a,b)].

3 `step` done is our base case, where done is an initial value, like 0 in foldr (+) 0 [1..3]. We don't want to zip anything after 3, because 3 is the final value of xs, so we must terminate the recursion. How do you terminate the recursion over list in the base case? You return empty list []. But recall done type signature:

done :: [b] -> [(a,b)]

Therefore we can't return just [], we must return a function that would ignore whatever it receives. Therefore use const:

done = const [] -- this is equivalent to done = \_ -> []

Now let's start figuring out what step should be. It combines a value of type a with a function of type [b] -> [(a,b)] and returns a function of type [b] -> [(a,b)].

In 3 `step` done, we know that the result value that would later go to our zipped list must be (3,6) (knowing from original xs and ys). Therefore 3 `step` done must evaluate into:

\(y:ys) -> (3,y) : done ys

Remember, we must return a function, inside which we somehow zip up the elements, the above code is what makes sense and typechecks.

Now that we assumed how exactly step should evaluate, let's continue the evaluation. Here's how all reduction steps in our foldr evaluation look like:

3 `step` done -- becomes
(\(y:ys) -> (3,y) : done ys)
2 `step` (\(y:ys) -> (3,y) : done ys) -- becomes
(\(y:ys) -> (2,y) : (\(y:ys) -> (3,y) : done ys) ys)
1 `step` (\(y:ys) -> (2,y) : (\(y:ys) -> (3,y) : done ys) ys) -- becomes
(\(y:ys) -> (1,y) : (\(y:ys) -> (2,y) : (\(y:ys) -> (3,y) : done ys) ys) ys)

The evaluation gives rise to this implementation of step (note that we account for ys running out of elements early by returning an empty list):

step x f = \[] -> []
step x f = \(y:ys) -> (x,y) : f ys

Thus, the full function zip is implemented as follows:

zip :: [a] -> [b] -> [(a,b)]
zip xs ys = foldr step done xs ys
  where done = const []
        step x f [] = []
        step x f (y:ys) = (x,y) : f ys

P.S.: If you are inspired by elegance of folds, read Writing foldl using foldr and then Graham Hutton's A tutorial on the universality and expressiveness of fold.

音盲 2024-07-14 11:43:31

一个简单的方法:

lZip, rZip :: Foldable t => [b] -> t a -> [(a, b)]

-- implement zip using fold?
lZip xs ys = reverse.fst $ foldl f ([],xs) ys
     where f  (zs, (y:ys)) x = ((x,y):zs, ys)

-- Or;
rZip xs ys = fst $ foldr f ([],reverse xs) ys
     where f x (zs, (y:ys))  = ((x,y):zs, ys)

A simple approach:

lZip, rZip :: Foldable t => [b] -> t a -> [(a, b)]

-- implement zip using fold?
lZip xs ys = reverse.fst $ foldl f ([],xs) ys
     where f  (zs, (y:ys)) x = ((x,y):zs, ys)

-- Or;
rZip xs ys = fst $ foldr f ([],reverse xs) ys
     where f x (zs, (y:ys))  = ((x,y):zs, ys)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文