26

I stumbled upon the following problem on a python challenge: Write a function that satisfies the following rule for any number of function calls.

f()()()()()(s) == 'fooooo' + s;

example:

f('it') == 'fit';
f()('x') == 'fox';
f()()('bar') == 'foobar';
f()()()('l') == 'foool';

The function should be stateless and should not use any variables outside the scope.

The function signature was:

def f(s=None):
    # Your code here

I thought that in order to be able to chain multiple calls we will have to return a function when no string is passed into the function, but can't figure out how to build the expected string with no external variables. Suggestions?

def f(s=None):
    if s is None:
        # concatenate an 'o'?
        return f
    else:
        # Last call, return the result str.
        return s
0

7 Answers 7

22

An alternative to Nikola's answer is something like this:

def f(s=None):
    if s: return f'f{s}'

    def factory(prefix):
        def inner(s=None):
            return f'f{prefix}{s}' if s else factory(prefix + 'o')
        return inner
    return factory('o')

using a closure and no helper function.

Sign up to request clarification or add additional context in comments.

Comments

13

Obviously, you need to store the number of 'o' somewhere in the memory (e.g. the code) of f. To achieve this, you can benefit from these 2 features of Python:

  1. You can define functions inside other functions
  2. There's this thing called argument binding which allows you to tell Python to fix the some or all of the arguments of your function. This is done through functools.partial

And here's the solution

from functools import partial

def f(s=None):
    # Define a new function g which takes the current text and takes s
    def g(current_text, s=None):
        if s is not None:
            return current_text + s
        else:
            # If called with an empty argument, just rebind current_text with an extra o
            return partial(g, current_text + "o")

    # Just call g with the initial conditions
    return g("f", s)

print(f()()()()()("s")) # fooooos
print(f("s")) # fs

6 Comments

you are using a class, that is defined outside of a scope
@user8426627, no, he is using a helper, but the function data lives whithin the scope.
@Netwave Well, actually, partial is indeed a class, and you could argue that it's also outside the scope. OTOH the call to partial could be trivially replaced with another nested function and a closure.
The function should be stateless and should not use any variables outside the scope., this do not mean that he cannot use any other resource, but that it should live in the function. At least is what I understood.
Brilliant solution, in my opinion fits the problem requirements!
|
4

You can try this:

def f(s=None):
    string = "f"
    def ret(p=None):
        nonlocal string
        string += "o"
        return ret if not p else string + p
    return ret if not s else string + s

3 Comments

OP said the function signature has to be def f(s=None):, so I don't know if this counts...
@sloth, sorry, I've missed that. Updated.
Brilliant solution, although it could be argued the stateless condition of it.
4

This is my go at it:

def f(x=None, seq=''):
    if x:
        return 'f' + seq + x
    else:
        def g(y=None, p=seq + 'o'):
            return f(y, p)
        return g

Edit If you really need the function signature to be f(x=None), use this:

def f(x=None):
    def f_(x=None, seq=''):
        if x:
            return 'f' + seq + x
        else:
            def g(y=None, p=seq + 'o'):
                return f_(y, p)
            return g
    return f_(x)

:^)

6 Comments

OP said the function signature has to be def f(s=None):, so I don't know if this counts...
That's not really a problem though, you can just wrap it into another function
or use nonlocal which have been implemented for this :D
But a variable, nonlocal or not, counts as state, doesn't it?
Don't know actually, depends on how to count.
|
2

Just for printing the string:

def f(s=None):
    def fo(s=None):
        if s==None:
            print('o',end='')
            return fo
        else:
            print(s)
            return
    if s!=None:
        print('f',end='')
        print(s)
        return
    elif s==None:
        print('fo',end='')
        return fo

Comments

1

Cute problem. This is a compact way to do it:

def f(s=None, prefix="f"):
    if s: return prefix + s
    return lambda s=None: f(s, prefix=prefix+"o")

Comments

-1

FP:

f=lambda s=None,prefix='f':prefix+s  if s else lambda s=None,prefix=prefix+'o':f(s,prefix)  

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.