Search code examples
pythonschemenested-loopsnested-listsmap-function

Chaining maps increasing list nesting level


I'm trying to do the follow Python conversion to Scheme to practice nested looping:

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

Here is my attempt to do it in 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))))

Now, it's close, but I'm wondering why each map produces a new level of listing? For example, how can I get the results to just be a list of triplets, such as:

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

Solution

  • It is generally a bad idea to try to take some code from an Algol-like language like Python and to "translate" it into lispy code. As mentioned by @Barmar, the Python code is not creating a list of results, it is only printing the results as a side-effect; the Scheme code is actually creating a list of results. The problem in the posted code is that the innermost map creates a list of lists, the intermediate map creates another list of lists from that, and the outermost map creates yet another list of lists from the nested lists it receives.

    You can make this work by strategically appending the results of the mapping operations:

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

    Here the innermost map over zs creates a list of lists. The intermediate map uses that result to create a list of lists of lists, which is appended together to get a list of lists. Finally the outermost map uses the list of lists provided by the inner two mapping operations to create another list of lists of lists, which is appended together to get the final result as a list of lists.

    Note that it is usually not a good idea to use append in loops like this; since append has linear time complexity by itself, these types of solutions tend to be quadratic or worse.

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