Racket/Scheme 中的 zip 函数

9

给定两个列表,返回一个列表,其元素为大小为二的列表,其中对于第i个列表,第一个元素是第一个原始列表的第i个元素,第二个元素是第二个原始列表的第i个元素。如果其中一个列表比另一个列表小,则生成的列表的大小与最小列表相同;如果其中一个列表为空,则返回一个空列表。例如:

> (zip '(1 2) '(3 4))
'((1 3) (2 4))

> (zip '(1 2 3) '())
'()
> (zip '() '(4 5 6))
'()
> (zip '(8 9) '(3 2 1 4))
'((8 3) (9 2))
> (zip '(8 9 1 2) '(3 4))
'((8 3) (9 4))

2
你目前尝试了什么?请贴出代码!否则人们会认为你想免费完成作业 ;) - Óscar López
6个回答

14

尝试这样做:

(map cons '(1 2 3) '(a b c))

左右:

(map list '(1 2 3) '(a b c))

(define zip (lambda (l1 l2) (map list l1 l2)))

->(zip '(1 2 3) '(x y z))
'((1 x) (2 y) (3 z))

3
(map cons '(1 2 3) '(a b c))的结果是((1 . a) (2 . b) (3 . c)),而不是期望的结果((1 a) (2 b) (3 c))。 - Sylwester
只要函数有两个输入参数,任何函数都可以在那里使用。 - alinsoar
出于好奇,为什么在“zip”定义中使用lambda?似乎(define (zip l1 l2) (map list l1 l2))也可以做到同样的效果...? - cat
你所写的是我所写的语法糖。你所写的形式在预处理期间被扩展到我所写的形式中。但是,在Haskell中,组合的更好的语法糖是通过组合(o运算符)或“section”的概念来实现的。在这种情况下,无需显式传递参数名称,因为Haskell会在预处理中为您完成这项工作。 - alinsoar
1
我认为在Racket中这样做并不正确 - 你只测试了输入长度相等的情况。此外,zip通常也适用于流,因此你可以通过(zip '(1 2 3) (in-naturals))创建一个索引对。 - George Mauer

4

因为你没有发布你编写的代码,所以我猜这是一道作业。我会给你一些提示,帮助你开始解决问题,以下是解决方案的一般结构,请填写空白部分-如果你能通过自己的方式得出正确答案,将会更有乐趣!

(define (zip lst1 lst2)
  (cond ((<???> lst1)  ; if the first list is empty
         <???>)        ; then return the empty list 
        ((<???> lst2)  ; if the second list is empty
         <???>)        ; then also return the empty list 
        (else          ; otherwise
         (cons (list   ; cons a list with two elements:
                <???>  ; the first from the first list
                <???>) ; and the first from the second list
               (zip <???> <???>))))) ; advance recursion over both lists

我使用样本输入测试了以上实现,结果与预期相符:

(zip '(1 2) '(3 4))
=> '((1 3) (2 4))

(zip '(1 2 3) '())
=> '()

(zip '() '(4 5 6))
=> '()

(zip '(8 9) '(3 2 1 4))
=> '((8 3) (9 2))

(zip '(8 9 1 2) '(3 4))
=> '((8 3) (9 4))

2
如果你已经解决了第一个元素的问题,那么可以对列表的其余部分进行递归:
(define (zip l1 l2)
  (if (or (null? l1) (null? l2))
      '()
      (cons (list (car l1) (car l2))
            (zip  (cdr l1) (cdr l2)))))

只要处理好其中一个列表为空的基本情况即可。

> (zip '(1 2 3 4) '(a b))
((1 a) (2 b))
> (zip '() '(a b))
()

1
如果我们接受 Racket 函数,并且放宽要求,不再要求返回 2 元组,而是采用更通用的 zip,那么我会查看 for/list。以下是将两个或三个列表压缩或交错的示例,停止于最短的列表
(define l1 '(a b c))
(define l2 '(1 2 3))
(define l3 '(true false))

;; → '((a 1 true) (b 2 false))
(for/list ([i l1] [j l2] [k l3])
          (list i j k))

;; → '((a 1) (b 2) (c 3))
(for/list ([i l1] [j l2])
          (list i j))

;; → '()
(for/list ([i l1] [j l2] [k null])
          (list i j k))

1
如果您的map实现停止于最短的列表,则可以使用map、Scheme的list过程和apply定义zip。这里有一个提示:
(define (zip . lsts)
  (apply <??> <??> lsts))

SRFI-1map足以满足需求。因此在Racket中,您需要添加(require (only-in srfi/1 map))


0
今天,我遇到了同样的练习,并做出了自己的实现,与所有人在这里发布的答案都不同。所有其他答案都很好。我真的很喜欢@Alinsoar最受欢迎的答案。
当然,其他答案实际上比我的实现更好。但是我仍然会发布它。也许,这可以帮助想要学习Racket的人。

(define (shorter-list xs ys)
  (if (> (length xs) (length ys))
      ys
      xs))

(define (zip xs ys)
  (cond [(null? (shorter-list xs ys)) null]
        [true (cons (list (car xs) (car ys)) (zip (cdr xs) (cdr ys)))]))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接