链接映射增加列表嵌套级别

Chaining maps increasing list nesting level

我正在尝试执行以下 Python 到 Scheme 的转换以练习嵌套循环:

>>> def possible_triads(n1, n2, n3):
        for num1 in n1:
             for num2 in n2:
                 for num3 in n3:
                     print (num1, num2, num3)
>>> possible_triads([1,2], [3,4], [5,6])
(1, 3, 5)
(1, 3, 6)
(1, 4, 5)
(1, 4, 6)
(2, 3, 5)
(2, 3, 6)
(2, 4, 5)
(2, 4, 6)

这是我在 Scheme 中的尝试:

(define (possible-triads n1 n2 n3)
  (map (lambda (num1)
         (map (lambda (num2)
                (map (lambda (num3) (list num1 num2 num3)) 
                     n3)) 
              n2)) 
       n1))
(possible-triads '(1 2) '(3 4) '(5 6))
;((((1 3 5) (1 3 6)) ((1 4 5) (1 4 6))) (((2 3 5) (2 3 6)) ((2 4 5) (2 4 6))))

现在,很接近了,但我想知道为什么每个 map 都会产生一个新的列表级别?例如,我怎样才能得到结果只是三元组的列表,例如:

(
    (1 3 5) 
    (1 3 6)
    (1 4 5)
    (1 4 6)
    (2 3 5)
    (2 3 6)
    (2 4 5)
    (2 4 6)
)

python for 循环不会自动将其结果收集到像 map 这样的列表中,因此您看不到嵌套。 Scheme 中的等价物是 for-each,它会因副作用而循环,而不是收集结果。就像在你的 Python 函数中一样,你应该在最里面的循环中打印列表,而不是 return 它。

(define (possible-triads n1 n2 n3)
  (for-each (lambda (num1)
    (for-each (lambda (num2)
      (for-each (lambda (num3) 
        (display (list num1 num2 num3))) n3)) n2)) n1))

尝试从类似 Algol 的语言(如 Python)中提取一些代码并将其“翻译”成 lispy 代码通常不是一个好主意。正如 所提到的,Python 代码没有创建结果列表,它只是将结果作为副作用打印出来; Scheme 代码实际上是在创建一个结果列表。发布的代码中的问题是最里面的 map 创建了一个列表列表,中间的 map 从中创建了另一个列表列表,最外面的 map 创建了另一个列表列表从它收到的嵌套列表中。

您可以通过策略性地附加映射操作的结果来完成这项工作:

(define (possible-triads xs ys zs)
  (apply append
         (map (lambda (x)
                (apply append
                       (map (lambda (y)
                              (map (lambda (z) (list x y z))
                                   zs))
                            ys)))
              xs)))

这里最里面的 mapzs 之上创建了一个列表列表。中间 map 使用该结果创建列表列表的列表,将其附加在一起以获得列表列表。最后最外层的map使用里面的两个映射操作提供的lists的lists创建另一个lists的lists的lists,将它们追加在一起得到最终的结果作为lists的lists。

请注意,在这样的循环中使用 append 通常不是一个好主意;由于 append 本身具有线性时间复杂度,因此这些类型的解决方案往往是二次或更糟的。

nested-looping.rkt> (possible-triads '(1 2) '(3 4) '(5 6))
'((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))