3

Can comprehensions be built programmatically to handle variable number of arguments as in Haskell?

For example, how to extend this outer product for dictionaries to more than 2 dictionaries:

def outer(f,g):
    return lambda d1,d2: {f(k1,k2):g(v1,v2) for k1,v1 in d1.items() for k2,v2 in d2.items()}

Given:

d1 = {'a':1, 'b':2}
d2 = {'a':10, 'b':11, 'c':12}

and these helper functions that work with variable args:

def string_join(*arg):
    return ''.join(tuple(arg))

def arg_sum(*arg):
    return sum(tuple(arg))

Then:

outer(string_join,arg_sum)(d1,d2)

{'aa': 11, 'ab': 12, 'ac': 13, 'ba': 12, 'bb': 13, 'bc': 14}

Can the convenient comprehension be extended to variable arg (d1,d2,d3...) or need to use for loops?

alancalvitti
  • 476
  • 3
  • 14

1 Answers1

1

The language doesn’t support this directly (since odometer control flow is very different at the bytecode level), but it can be implemented with itertools.product and extensive * unpacking:

def outer1(f,g,e):
  a,b=zip(*e)
  return f(*a),g(*b)
def outer(f,g):
  return lambda *dd: dict(outer1(f,g,e) for e in itertools.product(*(d.items() for x in dd)))
Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • @David, thanks will test this. What is 'odometer control flow'? – alancalvitti Jun 05 '19 at 16:20
  • @alancalvitti: Looping with a bignum (as typically assembled from an array primitive integers whose size is determined at runtime) involves an inner loop to implement carries, just as each wheel of an odometer turns the next when it rolls over. – Davis Herring Jun 05 '19 at 18:04