user7500570
user7500570

Reputation:

Using reduce to iterate over list of functions and call each one

Currently i'm using a list of strings with names of functions to fix the flow of my software:

flow = [
  "func1",
  "func2",
  "func3",
  "func4",
  "func5"
]

Then i iterate over the flow and call each one passing the options:

options = {}
[getattr(__import__(phase), phase)(options) for phase in flow]

I would like to know if is it possible to do the same, but avoiding side effects, using reduce. Currently, this approach it's making the functions receive the option, but isn't necessary return the options for the next function, so i'm changing the options that is declared in other scope.

Thanks.

Upvotes: 2

Views: 2178

Answers (2)

Amaury Larancuent
Amaury Larancuent

Reputation: 323

so reduce takes one a function, say reduce_func, that takes on 2 arguments. When it goes through a list it takes the first two items as the params of reduce_func for the first call, then on each subsequent call, uses the return value as the first param, and the next value on the list as the second param. This means, for you, reduce_func needs to be the following

def reduce_func(param, f):
   return f(param)

and your list needs to be the following:

[options, func1, func2, func3, func4]

Now, I used a list of functions and didn't use import. In stead of f, you could pass in [module].[function] as a string (call the param something like func_str), and do some splitting and inside of reduce_func as some set up.

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476584

You can use functools.reduce (which is sometimes called fold in other functional programming languages like Haskell) to indeed call the function.

In that case however you will need to define a function taking two parameters: the old accumulator value and the element itself. You simply ignore the old value and call the function on the element.

So for a generic function f(x), you can do this with:

functools.reduce(lambda _,x:f(x),list,initializer=0)

So in your case that would be:

options = {}
functools.reduce(lambda _,phase:getattr(__import__(phase),phase)(options),flow,initializer=0)

EDIT:

after rereading your question, it appears to me that each of the functions takes as input options, and generates the "new" options that should be passed to the next function. Well the return of the first function, is the first parameter of the lambda of the next function. So you can fold it together like:

first_options = {}
functools.reduce(lambda options,phase:getattr(__import__(phase),phase)(options),flow,initializer=first_options)

This will result in something equivalent to:

options_0 = first_options
options_1 = getattr(__import__(phase),flow[0])(options_0)
options_2 = getattr(__import__(phase),flow[1])(options_1)
# ...
return options_n

but of course this happens inside the reduce.

Upvotes: 2

Related Questions