3

I'm doing an exercise where I'm to create a class representing functions (written as lambda expressions) and several methods involving them.

The ones I've written so far are:

class Func():
    def __init__(self, func, domain):
        self.func = func
        self.domain = domain

    def __call__(self, x):
        if self.domain(x):
            return self.func(x)
        return None

    def compose(self, other):
        comp_func= lambda x: self.func(other(x))
        comp_dom= lambda x: other.domain(x) and self.domain(other(x))
        return Func(comp_func, comp_dom)

    def exp(self, k):
        exp_func= self
        for i in range(k-1):
            exp_func = Func.compose(exp_func, self)
        return exp_func 

As you can see above, the function exp composes a function with itself k-1 times. Now I'm to write a recursive version of said function, taking the same arguments "self" and "k". However I'm having difficulty figuring out how it would work. In the original exp I wrote I had access to the original function "self" throughout all iterations, however when making a recursive function I lose access to the original function and with each iteration only have access to the most recent composed function. So for example, if I try composing self with self a certain number of times I will get:

f= x+3
f^2= x+6
(f^2)^2= x+12

So we skipped the function x+9.

How do I get around this? Is there a way to still retain access to the original function?

Update:

def exp_rec(self, k):
    if k==1:
        return self
    return Func.compose(Func.exp_rec(self, k-1), self)
  • IMHO if you have to assign a lambda to a variable, then it's better to make a closure from it (i.e. use `def funcname(x):` instead of `funcname = lambda x: ...` – Błażej Michalik Jun 10 '17 at 17:48
  • Instructions state I'm to use lambda expressions. –  Jun 10 '17 at 17:53
  • 1
    Well ok then, just keep in mind that: https://stackoverflow.com/questions/25010167/e731-do-not-assign-a-lambda-expression-use-a-def – Błażej Michalik Jun 10 '17 at 17:54

1 Answers1

2

This is an exercise, so I won't provide the answer.

In recursion, you want to do two things:

  • Determine and check a "guard condition" that tells you when to stop; and

  • Determine and compute the "recurrence relation" that tells you the next value.

Consider a simple factorial function:

def fact(n):
    if n == 1:
        return 1

    return n * fact(n - 1)

In this example, the guard condition is fairly obvious- it's the only conditional statement! And the recurrence relation is in the return statement.

For your exercise, things are slightly less obvious, since the intent is to define a function composition, rather than a straight integer computation. But consider:

f = Func(lambda x: x + 3)

(This is your example.) You want f.exp(1) to be the same as f, and f.exp(2) to be f(f(x)). That right there tells you the guard condition and the recurrence relation:

The guard condition is that exp() only works for positive numbers. This is because exp(0) might have to return different things for different input types (what does exp(0) return when f = Func(lambda s: s + '!') ?).

So test for exp(1), and let that condition be the original lambda.

Then, when recursively defining exp(n+1), let that be the composition of your original lambda with exp(n).

You have several things to consider: First, your class instance has data associated with it. That data will "travel along" with you in your recursion, so you don't have to pass so many parameters recursively. Second, you need to decide whether Func.exp() should create a new Func(), or whether it should modify the existing Func object. Finally, consider how you would write a hard-coded function, Func.exp2() that just constructed what we would call Func.exp(2). That should give you an idea of your recurrence relation.

Update

Based on some comments, I feel like I should show this code. If you are going to have your recursive function modify the self object, instead of returning a new object, then you will need to "cache" the values from self before they get modified, like so:

func = self.func
domain = self.domain

... recursive function modifies self.func and self.domain
aghast
  • 14,785
  • 3
  • 24
  • 56
  • Thank you for the answer. However that doesn't really give me an idea of how to move forward, since as you said I should let exp(n+1) be the composition with the original lambda function, but how do I access that "original" function? The only argument exp can take are "self" and k. "k" clearly gives me no info on the original function,and so the recursion must take in some version of the original function. So after the first iteration, exp will no longer remember the original lambda function, but only the composition up to a certain k. So how am I to compose exp(n) with it? –  Jun 10 '17 at 18:18
  • Doesn't self have the function somewhere inside it? – aghast Jun 10 '17 at 19:03
  • At first. But only during the first step of the recursion. On the first step you compose self with self, creating self^2. Then you pass it to the next step of the recursion so self is now self^2. If you try to compose it with self again you will get self^4 rather than self^3. –  Jun 10 '17 at 19:22
  • That supposes you have made the decision that `exp()` modifies self (rather than return a separate object). In that case, store the lambda function in a local variable before recursing. *\[edit\]* I have added an update to demonstrate – aghast Jun 10 '17 at 19:43
  • I do not wish to modify the original self. But I can't pass the same self over to the next step of the recursion, otherwise I'm not really doing anything. When I call exp within exp then I'll have to call it on a composed version of self. Then on the next step exp doesn't know what the original self is. –  Jun 10 '17 at 19:54
  • Update your original post with a recursive version that only handles k=1. – aghast Jun 10 '17 at 20:27
  • I have updated it with the version that's causing me this problem (don't know I didn't post it before, lol). Perhaps now it's more clear what I mean? I know WHY this version doesn't work. I just don't know how to fix it. –  Jun 10 '17 at 20:57
  • Jackpot! I managed to make it work. Ii'll update my work solution. Thank you for your help and patience. :) –  Jun 10 '17 at 21:09
  • Congratulations. I knew you'd get it! – aghast Jun 10 '17 at 21:28