-3

I want to print Hello World n**n times without calculating the value of n**n in python.

eg,

if n is 2, it should print 'Hello World' 4 times.

if n is 3 it should print 'Hello World' 27 times and so on.

I am allowed to use loops and recursion but now allowed to use any inbuilt function or calculate the value of n **n and print that many times.

Thank you in advance.

binu.py
  • 1,137
  • 2
  • 9
  • 20
  • Range is allowed but your answer will give `n**2` always. I want `n**n` – binu.py Mar 21 '18 at 06:33
  • This is basically impossible (in Python 3), because `print` is also a built-in function. – Graipher Mar 21 '18 at 06:33
  • 1
    This is not "do my homework" site... What have you tried? – Umberto Mar 21 '18 at 06:33
  • This is not my homework. This is asked in an interview and i tried many things. I am able to reach square but not `n**n`. By inbuilt function it meant functions to calculate exponent like `**` or math.power etc. – binu.py Mar 21 '18 at 06:34
  • Write an `ntimes` higher-order function that applies its argument to its argument's argument n times in a row. Call `ntimes` on itself with `n`, then call the result on `partial(print, 'Hello World')`. – abarnert Mar 21 '18 at 06:35
  • @abarnert I am still not clear. can you please give it a try? – binu.py Mar 21 '18 at 06:38
  • @Graipher Nope. Well, yes, but it's the same as `n` nested for loops instead of two. That's how we get `n**n` instead of `n**2`. And that should give you a nice clue on how to write this pythonically with `itertools`, or how to write a simple recursive version, but I wanted to write it differently, so… see my answer. – abarnert Mar 21 '18 at 06:56

3 Answers3

4

First:

def compose(f, g):
    def wrapper(x):
        return f(g(x))
    wrapper.__name__ = f'compose({f.__name__}, {g.__name__})'
    return wrapper

def ntimes(n):
    def wrap(func):
        if n == 1: return func
        return compose(func, ntimes(n-1)(func))
    return wrap

That should be obvious, right? ntimes(3) is a function that composes any function with itself 3 times, so ntimes(3)(func)(x) is func(func(func(x))).

And now, we just need to call ntimes on ntimes with the same n at both levels. I could write an nntimes function that does that the same way ntimes did, but for variety, let's make it flatter:

def nntimes(n, func, arg):
    f = ntimes(n)
    return f(f)(func)(arg)

So nntimes(n, func, arg) calls ntimes(n) on ntimes(n), which gives you a function that composes its argument n**n times, and then calls that function on arg.

And now we just need a function to pass in. print doesn't quite work, because it returns None, so you can't compose it with itself. So:

def printret(x):
    print(x, end=' ')
    return x

And now we just call it:

>>> nntimes(2, printret, 'Hi')
hi hi hi hi
>>> nntimes(3, printret, 'Hi')
hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi

If you still can't understand what's happening, maybe this will help. Let's do something a bit simpler than the general nntimes and just hardcode three, and then print out the composition:

>>> thrice = ntimes(3)
>>> print(thrice(thrice)(printret).__name__)
compose(compose(compose(printret, compose(printret, printret)), compose(compose(printret, compose(printret, printret)), compose(printret, compose(printret, printret)))), compose(compose(compose(printret, compose(printret, printret)), compose(compose(printret, compose(printret, printret)), compose(printret, compose(printret, printret)))), compose(compose(printret, compose(printret, printret)), compose(compose(printret, compose(printret, printret)), compose(printret, compose(printret, printret))))))

All those parentheses! It's like I've died and gone to Lisp!


If you read up on Church numerals, you'll see that I've sort of cheated here. Write up the trivial functions to Church-encode a number and to exponentiate two Church numerals, then compare it to what my code does. So, have I really avoided calculating the value of n**n?


Of course you can do this a whole lot more simply with a simple flat recursion and no higher-order functions, or with itertools (well, you're not allowed to use builtins, but everything in itertools comes with source and/or or a "roughly equivalent" function in the docs, so you can just copy that). But what's the fun in that? After all, if you actually wanted a Pythonic, or simple, or efficient version, you'd just loop over range(n**n). I assume the point of this interview question is to force you to think outside the Pythonic box.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • It works great. Thank you. I am still a bit confused and trying to understand it step by step. – binu.py Mar 21 '18 at 07:09
  • @binu.py Great! Try working it through with 1, then 2, before tackling 3. (Don't try 0, because I forgot to handle that…) Maybe try `thrice(twice)` and `twice(thrice)`, too. Also, think about higher-order functions you _do_ understand, like `map` or `functools.partial`, and work through how they work. – abarnert Mar 21 '18 at 07:17
  • Sure. Thanks @abarnert i understood it now. – binu.py Mar 21 '18 at 07:19
  • Rereading this the next day, with more coffee in my bloodstream… why didn't I write this up in terms of encoding the ints in Church numerals? That would allow a useful explanatory link, while at the same time allowing me to make the code even more ridiculous for Python. – abarnert Mar 21 '18 at 19:17
0
>>> def new(number,loop):
...     if loop == 1:
...         return number
...     else:
...         return number * new(number,loop-1)
... 
>>> a = new(3,3)
>>> print a
27
>>> print "Hello "*a
Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello 

  • I have already tried this. But i am not allowed to calculate the value of n**n just need to print – binu.py Mar 21 '18 at 07:27
0

Just for fun and to give an instructive answer you could do it this way with a very simple recursive function without calculating n**n:

def fun(n, level):
    if (level == n):
        print('Hello World')
        return 
    for i in range (n):
        fun(n, level+1)

And if you try

fun(2,0)

you get

Hello World
Hello World
Hello World
Hello World

the same works for fun(3,0)... Hope that helps. Is a rather trivial solution. @abarnert wonderful solution!

Best, Umberto

Umberto
  • 1,387
  • 1
  • 13
  • 29