how to write function that returns function
Author |
Message |
Paul Grah #1 / 29
|
 how to write function that returns function
I am not a python expert, and I'm hoping someone can tell me how in Python to write a function of one argument x that returns a function of one argument y that returns x+y. Here, in Scheme, is what I want to write: (define foo (x) (lambda (y) (+ x y))) I found on the web a page that says I could define this as follows: def addn(x): return lambda y,z=y: x+z but I don't think this is exactly the same thing, because it returns a function that takes a second optional argument. That is a substantial difference. If the Scheme function is inadvertently called (e.g. in someone else's code) with two arguments, it would signal an error, whereas the code above would quietly give the wrong answer. I would appreciate it if someone could tell me the standard way to write this so that it returns a function of exactly one argument. Thanks!
|
Sun, 31 Oct 2004 07:03:25 GMT |
|
 |
Ian Bickin #2 / 29
|
 how to write function that returns function
Quote:
> I found on the web a page that says I could define > this as follows: > def addn(x): > return lambda y,z=y: x+z > but I don't think this is exactly the same thing, > because it returns a function that takes a second > optional argument. That is a substantial difference. > If the Scheme function is inadvertently called > (e.g. in someone else's code) with two arguments, it > would signal an error, whereas the code above would > quietly give the wrong answer.
This is the typical way of doing it, and yes, it is somewhat flawed. In Python 2.1+ you can do: from __future__ import nested_scopes def addn(x): return lambda y: x+y Ian
|
Sun, 31 Oct 2004 07:10:26 GMT |
|
 |
Bengt Richt #3 / 29
|
 how to write function that returns function
Quote: >I am not a Python expert, and I'm hoping someone >can tell me how in Python to write a function >of one argument x that returns a function of one >argument y that returns x+y. >Here, in Scheme, is what I want to write: >(define foo (x) (lambda (y) (+ x y)))
With nested scopes (default in 2.2, import nested_scopes from __future__ in 2.1), you can do this: >>> def foo(x): return lambda y: x+y ... >>> f = foo(7) >>> f(3) 10 >>> import dis <-- handy disassembler for seeing what functions do etc. >>> dis.dis(f) 0 SET_LINENO 1 3 LOAD_DEREF 0 (x) <-- this loads the 7 from the closure 6 LOAD_FAST 0 (y) <-- this loads the argument to the function 9 BINARY_ADD 10 RETURN_VALUE >>> f.func_closure (<cell at 0x008430C0: int object at 0x00795910>,) >>> hex(id(7)) '0x795910' Doing it as above captures x in a closure. I used a small int 7 knowing the same instance would be used, and we could recognize it in the closure by its id. Quote: >I found on the web a page that says I could define >this as follows: >def addn(x): > return lambda y,z=y: x+z
I don't think that's quite right. The idea is to capture x in a way that works like the closure, and a dummy default value is the mechanism, so I think it should be: >>> def foo(x): return lambda y,z=x: y+z ... >>> f = foo(7) >>> f(3) 10 >>> dis.dis(f) 0 SET_LINENO 1 3 LOAD_FAST 0 (y) <-- this loads the first function arg 6 LOAD_FAST 1 (z) <-- this simulates the closure effect 9 BINARY_ADD \_ unless you pass a second arg 10 RETURN_VALUE >>> f.func_closure <-- there isn't any (None) >>> f.func_defaults (7,) >>> f(3,5) <-- the second arg overrides, so it's not a proper solution 8 Quote: >but I don't think this is exactly the same thing, >because it returns a function that takes a second >optional argument. That is a substantial difference. >If the Scheme function is inadvertently called >(e.g. in someone else's code) with two arguments, it >would signal an error, whereas the code above would >quietly give the wrong answer.
right Quote: >I would appreciate it if someone could tell me the >standard way to write this so that it returns a >function of exactly one argument.
See above, but BTW, you don't have to use lambda at all: >>> def foo(x): ... def forget_this_name(y): ... return x+y ... return forget_this_name ... >>> f = foo(7) >>> f(3) 10 >>> dis.dis(f) 0 SET_LINENO 2 3 SET_LINENO 3 6 LOAD_DEREF 0 (x) <-- this loads the 7 from the closure 9 LOAD_FAST 0 (y) <-- this loads the function arg 12 BINARY_ADD 13 RETURN_VALUE 14 LOAD_CONST 0 (None) <--this is dead boilerplate code you don't get with lambda 17 RETURN_VALUE >>> f.func_closure (<cell at 0x008430C0: int object at 0x00795910>,) <-- proper closure this time. Note hex location >>> f.func_defaults <-- no defaults >>> f(3,5) <-- strictly one arg, so this fails Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: forget_this_name() takes exactly 1 argument (2 given) >>> hex(id(7)) '0x795910' <--hex location of 7 HTH Regards, Bengt Richter
|
Sun, 31 Oct 2004 09:37:23 GMT |
|
 |
Steve Holde #4 / 29
|
 how to write function that returns function
Quote: > I am not a Python expert, and I'm hoping someone > can tell me how in Python to write a function > of one argument x that returns a function of one > argument y that returns x+y. > Here, in Scheme, is what I want to write: > (define foo (x) (lambda (y) (+ x y))) > I found on the web a page that says I could define > this as follows: > def addn(x): > return lambda y,z=y: x+z > but I don't think this is exactly the same thing, > because it returns a function that takes a second > optional argument. That is a substantial difference. > If the Scheme function is inadvertently called > (e.g. in someone else's code) with two arguments, it > would signal an error, whereas the code above would > quietly give the wrong answer. > I would appreciate it if someone could tell me the > standard way to write this so that it returns a > function of exactly one argument.
There's no need to use lambda, and indeed I eschew it whenever it make sense to do so. From 2.2 on you don't need top import nested_scopes from __future__, by the way. Since functions are fisrt-class objects, it doesn't matter what name is associated with the declarations - they can be bound to any variable once they are returned. Python 2.2 (#1, Dec 31 2001, 15:21:18) [GCC 2.95.3-5 (cygwin special)] on cygwin Type "help", "copyright", "credits" or "license" for more information. Quote: >>> def adder(x):
... def anyoldname(y): ... return x+y ... return anyoldname ... Quote: >>> add3 = adder(3) >>> add100 = adder(100) >>> add3(12) 15 >>> add100(12) 112
Note, however, that the function name (the name bound to it in the def statement) does get built in to its repr(): Quote: >>> add3
<function anyoldname at 0x100fffe8> Quote: >>> add100
<function anyoldname at 0x10100278>
Hope this helps. regards Steve -- ----------------------------------------------------------------------- Steve Holden http://www.holdenweb.com/ Python Web Programming http://pydish.holdenweb.com/pwp/ -----------------------------------------------------------------------
|
Sun, 31 Oct 2004 10:28:43 GMT |
|
 |
Fernando RodrĂgue #5 / 29
|
 how to write function that returns function
Quote: >I am not a Python expert, and I'm hoping someone >can tell me how in Python to write a function >of one argument x that returns a function of one >argument y that returns x+y. >Here, in Scheme, is what I want to write: >(define foo (x) (lambda (y) (+ x y))) >I found on the web a page that says I could define >this as follows: >def addn(x): > return lambda y,z=y: x+z
Get a recent version of Python that includes 'nested scopes' a la Lisp, and try: Quote: >>> from __future__ import nested_scopes # Turn on nested scopes >>> def f(x):
def g(y): return y +x return g Quote: >>> adder = f(3) >>> adder(4)
7 BTW, this is not exactly the same behavior you find in Lisp. For example: x = 0 #Global variable def f(): x = 3 # Doesnt create a dynamic binding, it creates a new local variable return x The 'x = 3' doesn't "setf", it "let"s I find this behavior disturbing, and I hope special variables in Arc will behave as in Common Lisp. Good luck with Arc! :-) ----------------------- Fernando Rodriguez
|
Sun, 31 Oct 2004 15:21:46 GMT |
|
 |
Paul Grah #6 / 29
|
 how to write function that returns function
Thanks to everyone who replied to my earlier question. It seems Python scope rules have changed recently, and my info was out of date. I am still uncertain about a couple things though: there seem to be some restrictions on what you can do with lexical variables and also what you can put in a lambda. Can some Python expert tell me how you would express the Common Lisp (defun foo (n) #'(lambda () (incf n))) in Python? Many thanks, --pg
|
Mon, 01 Nov 2004 00:15:53 GMT |
|
 |
Michael Hudso #7 / 29
|
 how to write function that returns function
Quote:
> Thanks to everyone who replied to my earlier question. It seems > Python scope rules have changed recently, and my info was out of > date. I am still uncertain about a couple things though: there > seem to be some restrictions on what you can do with lexical > variables and also what you can put in a lambda. Can some Python > expert tell me how you would express the Common Lisp > (defun foo (n) #'(lambda () (incf n))) > in Python?
You can't directly translate that. Access to variable of enclosing scopes is read only (except in the case of global statements, but that doesn't help here). The usual response to questions like this is "what is it you're actually trying to *do*?"; there may well be a natural way of acheiving *that* despite the fact that there are no non-horrible direct translations of the above code (usually involving defining a class or two...). Cheers, M. -- It's a measure of how much I love Python that I moved to VA, where if things don't work out Guido will buy a plantation and put us to work harvesting peanuts instead. -- Tim Peters, comp.lang.python
|
Mon, 01 Nov 2004 00:34:08 GMT |
|
 |
George Demm #8 / 29
|
 how to write function that returns function
Quote:
> Thanks to everyone who replied to my earlier question. It seems > Python scope rules have changed recently, and my info was out of > date. I am still uncertain about a couple things though: there > seem to be some restrictions on what you can do with lexical > variables and also what you can put in a lambda. Can some Python > expert tell me how you would express the Common Lisp > (defun foo (n) #'(lambda () (incf n))) > in Python? > Many thanks, --pg
Here's one way: class foo: def __init__(self, n): self.n = n def next(self): self.n += 1 return self.n def mkfoo(n): f = foo(n) return f.next bar = foo(1) print bar(), bar(), bar() -> 2 3 4 I don't think that the "Guido implementation" of Python allows the capture of state in closures the same way that you can in Scheme and CL, though you can fake it very easily, as above. G
|
Mon, 01 Nov 2004 01:38:26 GMT |
|
 |
George Demm #9 / 29
|
 how to write function that returns function
Correction: Quote:
> > Thanks to everyone who replied to my earlier question. It seems > > Python scope rules have changed recently, and my info was out of > > date. I am still uncertain about a couple things though: there > > seem to be some restrictions on what you can do with lexical > > variables and also what you can put in a lambda. Can some Python > > expert tell me how you would express the Common Lisp > > (defun foo (n) #'(lambda () (incf n))) > > in Python? > > Many thanks, --pg > Here's one way: > class foo: > def __init__(self, n): > self.n = n > def next(self): > self.n += 1 > return self.n > def mkfoo(n): > f = foo(n) > return f.next > bar = foo(1)
bar = mkfoo(1) Quote: > print bar(), bar(), bar() > -> 2 3 4
G
|
Mon, 01 Nov 2004 01:49:06 GMT |
|
 |
George Demm #10 / 29
|
 how to write function that returns function
Playing around a bit... CL: (defun foo (n) #'(lambda () (incf n))) Python: def foo(n=0): class bar: def __init__(self): self.n = n def next(self): self.n += 1 return self.n return bar().next f = foo() print f(),f(),f() -> 2 3 4 g = foo(1) -> 3 4 5 Arc: ? ;) G
|
Mon, 01 Nov 2004 02:22:26 GMT |
|
 |
David Eppstei #11 / 29
|
 how to write function that returns function
Quote: > CL: > (defun foo (n) #'(lambda () (incf n))) > Python: > def foo(n=0): > class bar: > def __init__(self): > self.n = n > def next(self): > self.n += 1 > return self.n > return bar().next > f = foo() > print f(),f(),f() > -> 2 3 4 > g = foo(1) > -> 3 4 5
You seem to have an off by one error on your output: I get 1 2 3 and 2 3 4 Anyway, as long as you're going to do that, why not: from __future__ import generators def foo(n=0): def bar(n=n): while 1: n += 1 yield n return bar().next [ from earlier message by same author: ] Quote: > I don't think that the "Guido implementation" of Python allows the > capture of state in closures the same way that you can in Scheme and > CL, though you can fake it very easily, as above.
I think the key difference is that Python's closures only give you read access to the variables. If you want write access you have to encapsulate them in something else, like a list: def foo(n=0): n=[n] def bar(): n[0] += 1 return n[0] return bar -- David Eppstein UC Irvine Dept. of Information & Computer Science
|
Mon, 01 Nov 2004 02:44:44 GMT |
|
 |
Kragen Sitake #12 / 29
|
 how to write function that returns function
Quote:
> def addn(x): > return lambda y,z=y: x+z
(assuming you mean lambda y,z=x: x+z) Quote: > but I don't think this is exactly the same thing, > because it returns a function that takes a second > optional argument. That is a substantial difference.
This is embarrassing, which I guess is why we have nested scopes in 2.1+ so we don't have to do this any more; but here's a solution for 1.5.2: class addn: def __init__(self, x): self.x = x def __call__(self, y): return self.x + y This is a class instead of a function, but that is not a substantial difference. Unlike the nested-scopes case, this lets you modify the state of the object in a straightforward way; a "counter" closure in Python is ugly: def counter(startvalue): state = [startvalue] def counter_internal(): state[0] += 1 return state[0] return counter_internal The class equivalent is a little better: class counter: def __init__(self, startvalue): self.state = startvalue def __call__(self): self.state = self.state + 1 return self.state (I didn't use += because this class version will work in 1.5.2 and earlier Pythons, which don't have +=. The nested-scopes version doesn't.)
|
Mon, 01 Nov 2004 05:34:39 GMT |
|
 |
Paul Grah #13 / 29
|
 how to write function that returns function
Quote: > You can't directly translate that. Access to variable of enclosing > scopes is read only (except in the case of global statements, but that > doesn't help here).
It looks as if the closest thing would be something like this, which a Python expert sent me: def foo(n): s = [n] def bar(i): s[0] += i return s[0] return bar Although you can't modify variables from outer scopes, you can modify *parts* of them (which to me seems like the same thing...) The reason this problem seems kind of artificial is that I don't need to solve this actual problem. I heard that recent Python versions had added more support for lexical closures, and was curious how well Python works for the type of programming in Structure and Interpretation of Computer Programs; this is kind of a hello_world of that genre. (It's not a homework problem, I swear.) Incidentally, here is Perl 5 code someone sent me for this case: sub foo {
return sub {return $n += $_[0]}} --pg
|
Mon, 01 Nov 2004 07:04:05 GMT |
|
|
Page 1 of 2
|
[ 29 post ] |
|
Go to page:
[1]
[2] |
|