Scheme制作两个相等长度列表中元素的所有对排列的列表

时间:2016-05-20 00:29:49

标签: recursion scheme nested-loops mit-scheme

我试图在方案中将两个x坐标和y坐标列表组合成对,我很接近,但无法获得返回的对列表。 以下内容可以使用嵌套循环匹配所有对,但我不确定将它们放到最佳方式,现在我只是将它们显示到控制台。

(define X (list 1 2 3 4 5))
(define Y (list 6 7 8 9 10))
(define (map2d X Y)
    (do ((a  0 (+ a 1)))               ; Count a upwards from 0
        ((= a (length X) ) )           ; Stop when a = length of list
      (do ((b 0 (+ b 1)))              ; Count b upwards from 0
          ((= b (length Y) ) )         ; Stop when  b = length of second list
          (display (cons (list-ref X a) (list-ref Y b))) (newline)
        ))
)
(map2d X Y)

我希望有这个功能输出

((1 . 6) (1 . 7) (1 . 8) ... (2 . 6) (2 . 7) ... (5 . 10))

然后我将使用map将此列表提供给另一个成对的函数。

奖励积分如果你可以帮助我让它更加递归(不是纯粹的功能,对吗?),这是我第一次使用函数式编程而且递归并不容易把握。谢谢!

2 个答案:

答案 0 :(得分:1)

使用do并不是非常惯用。您可以尝试嵌套map s,这更符合Scheme的精神 - 使用内置的高阶程序是可行的方法!

; this is required to flatten the list
(define (flatmap proc seq)
  (fold-right append '() (map proc seq)))

(define (map2d X Y)
  (flatmap
   (lambda (i)
     (map (lambda (j)
            (cons i j))
          Y))
   X))

很遗憾你没有使用Racket,这会更好:

(define (map2d X Y)
  (for*/list ([i X] [j Y])
    (cons i j)))

答案 1 :(得分:1)

ÓscarLópez的解决方案是正确而优雅的,并使您能够以功能语言编程的“正确”方式。但是,既然你开始研究递归,我将提出一个简单的递归解决方案,没有高级函数:

(define (prepend-to-all value y)
  (if (null? y)
      '()
      (cons (cons value (car y)) (prepend-to-all value (cdr y)))))

(define (map2d x y)
  (if (null? x)
      '()
      (append (prepend-to-all (car x) y) (map2d (cdr x) y))))

函数map2d在第一个列表中重复出现:如果它为空,则笛卡尔积为空;否则,它将收集通过将x的第一个元素添加到y的所有元素之前获得的所有对,并将所有对通过将其自身应用于x的其余部分而获得y的元素。

函数prepend-to-all将生成从单个值value和列表y的所有元素构建的所有对。它在第二个参数列表上重复出现。当y为空时,结果为空对列表,否则,它会与valuey的第一个元素构建一对,并在前置结果上“汇总”它value的所有剩余元素y

当你掌握递归时,你可以通过学习尾递归来传递到下一步,其中对函数的调用不包含在其他一些“构建”形式中,但是是第一个递归的呼叫。这种形式的优点是编译器可以将其转换为(更多)更有效的迭代程序。以下是适用于您的问题的此技术示例:

(define (map2d x y)
  (define (prepend-to-all value y pairs)
    (if (null? y)
        pairs
        (prepend-to-all value (cdr y) (cons (cons value (car y)) pairs)))) 
  (define (cross-product x y all-pairs)
    (if (null? x)
        (reverse all-pairs)
        (cross-product (cdr x) y (prepend-to-all (car x) y all-pairs))))
  (cross-product x y '()))

关键思想是使用新参数定义一个辅助函数,该参数在构建时“累积”结果。这个“累加器”在辅助函数的调用中用()初始化,将作为递归的终端情况返回。在这种情况下情况更复杂,因为有两个函数,但您可以研究新版本的prepend-to-all以查看其工作原理。请注意,要以自然顺序返回所有对,请在cross-product函数的末尾反转结果。如果您不需要此订单,则可以省略reverse以使该功能更有效。