440,172 Members | 727 Online Need help? Post your question and get tips & solutions from a community of 440,172 IT Pros & Developers. It's quick & easy.

# Anonymus functions revisited : tuple actions

 P: n/a Hi all, thanks for Your attention ! I think my proposal was more in mind of Rons modified exec than Pythons lambda. When George proposed his unpacking behavoir for list-comps as a pack of suggar: 1. [x*y-z for (x,y,z=0) in (1,2,3), (4,5), (6,7,8)] I interpreted it in a subsequent posting in lambda fashion: 2. [(lambda x,y,z=0:x*y-z)(*v) for v in (1,2,3), (4,5), (6,7,8)] which would be equivalent, if Georges syntax became standard Python. This motivates a fresh view on tuples as unpacking actions on other tuples which could be generalized to anonymus functions which are quite similar to Pythons lambda but are NOT the Python lambda because of different scoping rules which was addressed lately by Rons and Georges postings in this thread. Remember the already admissable expression: [x*y-z for (x,y,z) in (1,2,3),(4,5,6)] [-1,14] x,y (4,5) x and y are bound variables that are visible outside the list-comp ! So it would be with (x,y,z) -> (x,y,z) when it is applied to any tuple (a,b,c) with 3 elements. It does not return (a,b,c) but (x,y,z) with x = a, y = b, z = c. This is the reason why it is simple to chain tuple-actions. In 1. we have actually two different tuple actions: f = (x,y,z=0) -> (x,y,z) g = (x,y,z) -> x*y-z which is chainable by g o f. If we expand the notion of tuple-action in the list-comp 1. we get [(x,y,z)->x*y-z for (x,y,z=0)->(x,y,z) in (1,2,3), (4,5), (6,7,8)] which is still a short form of the explicit [((x,y,z)->x*y-z)(x,y,z) for (x,y,z=0)->(x,y,z) in (1,2,3), (4,5), (6,7,8)]. There is still no such thing as a free/unbound variable as in real lambda calculus that can be bound by another lambda. Tuple actions would obviously sweep off this misguided association - o.k not so for Pythonistas ;) Maybe Guidos aversion against FP is bit misguided too because it is actually in the language but hidden as special rules. Regards Kay Jul 18 '05 #1
12 Replies

 P: n/a Kay Schluehr wrote: Hi all, thanks for Your attention ! I think my proposal was more in mind of Rons modified exec than Pythons lambda. When George proposed his unpacking behavoir for list-comps as a pack of suggar: 1. [x*y-z for (x,y,z=0) in (1,2,3), (4,5), (6,7,8)] I interpreted it in a subsequent posting in lambda fashion: 2. [(lambda x,y,z=0:x*y-z)(*v) for v in (1,2,3), (4,5), (6,7,8)] Argh! Stupid me ! I never thought of using the star operator for this :( Thanks Kay. (snip) -- bruno desthuilliers python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for p in 'o****@xiludom.gro'.split('@')])" Jul 18 '05 #2

 P: n/a On Wed, 23 Mar 2005 06:21:30 +0100, Kay Schluehr wrote: I think my proposal was more in mind of Rons modified exec thanPythons lambda.When George proposed his unpacking behavoir for list-comps as a pack ofsuggar:1. [x*y-z for (x,y,z=0) in (1,2,3), (4,5), (6,7,8)]I interpreted it in a subsequent posting in lambda fashion:2. [(lambda x,y,z=0:x*y-z)(*v) for v in (1,2,3), (4,5), (6,7,8)] Thank you Kay, All of this is really intersting and I'm learning a lot about the language through these discussions. The following is an experiment I did this morning. :-) I was surprised it worked as well as it did, although I don't think it should be used in any real production code. Not in it's present form anyway. The idea is to have a container class like a tuple for program code that can be moved around and used when needed. Very flexable, maybe if it could be done without the strings and the exec/eval() functions in it? Ron_Adam # codedo.py import types class code(tuple): """ Inline Code Storage Class name = code(('expression','expression',...)) varables = name.do([locals()],['invars'],'outvars') This is experimental. Warning: This is experimental! This class has not been tested. It also uses exec, and eval(), which can be a security risk. """ def do(self, *args ): if type(args) == type({}): parentnames = args else: parentnames = globals() if len(args)>1: argslist = args.split(',') else: argslist = args for a in argslist: if parentnames.has_key(a): exec a+'=parentnames[a]' for c in self: exec(c) return eval(args[-1]) # The last argument are the return varable(s). if __name__ == '__main__': """ Test it. This is only what works, not what doesn't. """ # Left to Right order. y=3 print code(('y=y*2','x=y**2')).do('x') # *** Define and use later! *** mybutton_action = code(('z=y*2','x=z**2','result=x+2')) y = 1 print mybutton_action.do('y','result') y = 10 print mybutton_action.do('y','result') y = 100 print mybutton_action.do('y','result') # Return multiple values. toxyz = code(('x*=2','y*=2','try:z\nexcept:z=0','z*=2')) x = 2 y = 3 #z = 4 a, b, c = toxyz.do('x,y,z') print a, b, c # 1. [x*y-z for (x,y,z=0) in (1,2,3), (4,5), (6,7,8)] print code(('r=[]','for x,y,z in [(1,2,3),(4,5,0),(7,8,9)]:r.append(x*y-z)')).do('r') # or... trailing comma needed here to make a uni-tuple. print code(('r=list([x*y-z for x,y,z in (1,2,3),(4,5,0),(7,8,9)])',)).do('r') # post process list before returning. print code(('r = [ x for x in range(1,11) ]','r=r*2')).do('r') # From within a function: # We need to pass locals() to so it can find the variables. def fn1(): x = 5 y = 10 lfunction = code(('z = x*2+y',)).do(locals(),'x,y','z') print lfunction fn1() Jul 18 '05 #3

 P: n/a Ron wrote: On Wed, 23 Mar 2005 06:21:30 +0100, Kay Schluehr wrote:I think my proposal was more in mind of Rons modified exec thanPythons lambda.When George proposed his unpacking behavoir for list-comps as a pack ofsuggar:1. [x*y-z for (x,y,z=0) in (1,2,3), (4,5), (6,7,8)]I interpreted it in a subsequent posting in lambda fashion:2. [(lambda x,y,z=0:x*y-z)(*v) for v in (1,2,3), (4,5), (6,7,8)] Thank you Kay, All of this is really intersting and I'm learning a lot about the language through these discussions. Thanks, Ron ! The following is an experiment I did this morning. :-) I was surprised it worked as well as it did, although I don't think it should be used in any real production code. Not in it's present form anyway. The idea is to have a container class like a tuple for program code that can be moved around and used when needed. Very flexable, maybe if it could be done without the strings and the exec/eval() functions in it? I personally don't like using exec and eval for stuff different from evaluating user input. You rely much on "evaluate statement on the line" by adapting conventional Python syntax. I think one can go a bit further breaking the syntactical prejudices and apply tuple-actions :) Playing a bit with tuple-actions shows that the concept is quite powerfull and can be used to create simple statements. First of all the semantics has to be patched: We have (x,y,z=0) -> (x,y,z) as a tuple assignment ((x,y,z=0)->(x,y,z))(a,b,c) = (x=a,y=b,z=c) But it is not clear what (x,y,z=0) -> x*y-z actually means? Proposal: (x,y=0) -> x*y => ((x,y=0)->x*y) (a,b) -> (x=a,y=b),a*b (x,y=0) -> (x*y) => ((x,y=0)->(x*y))(a,b) -> (x=a*b,y=b) So (x,y=0) -> x*y is appending the result to the argument tuple. Remark: this is isomorph to (x,y=0,res=None) -> ((x,y),x*y) but it becomes harder now to identify (x,y,res=None) -> ((x,y),x*y) with x*y Provide a compiler-hint: (x,y,()) -> x*y Now we are ready for a few examples: default value: (i) -> (0) # i = 0 inplace increment: (i) -> i+1 # i = i+1 conditional expression: (i) -> i<3 # i,res = i,i<3 simple transformation: (res) -> (res+i**2) # res = res+i**2 Define a While loop as a function: def While( par, cond, change, action): par(None) # create default res = 0 while cond(par): action(res) change(par) return res Let's apply it to some tuple actions: While((i)->(0), (i)->i<3, (i)->(i+1), (res)->(res+i**2)) and evaluate While stepwise: 1. par(None) <=> (i)->(0)(None) # (i) = (0) 2. cond(par) <=> (i)->i<3(0) # (i,c) = (0,True) 3. action(res) <=> (res) -> (res+i**2)(0) # (res) = (0) 4. change(par) <=> (i)->(i+1)(0) # (i) = (1) 5. cond(par) <=> (i)->i<3(1) # (1,c) = (0,True) 6. action(res) <=> (res) -> (res+i**2)(0) # (res) = (1) 7. change(par) <=> (i)->(i+1)(1) # (i) = (2) 5. cond(par) <=> (i)->i<3(2) # (2,c) = (0,True) 6. action(res) <=> (res) -> (res+i**2)(1) # (res) = (5) 7. change(par) <=> (i)->(i+1)(2) # (i) = (3) 5. cond(par) <=> (i)->i<3(2) # (2,c) = (0,False) break => res = 5 If we customize the other control flow primitives For and If it should be possible to create a little language only by using this primitives. It is obvious by definition of our While that we can replace arguments on the fly: conds = [(i)->i<3, (i)->i+2<7, (i)->i>=0] [ While((i)->(0), cond, (i)->(i+1), (res)->(res+i**2)) for cond in conds] => [5,29,0] Wouldn't it be fun to use in Python? Only drawback: does not look like executable pseudo-code anymore :( Regards Kay Jul 18 '05 #4

 P: n/a "Kay Schluehr" wrote: [snipped] Wouldn't it be fun to use in Python? Only drawback: does not look like executable pseudo-code anymore :( Regards Kay I don't know if it would be fun, but it certainly doesn't look accessible to mere mortals :-) I'm not sure if the mind boggling is more due to the syntax with the '->' and all or the semantics, but it goes in the oppposite direction from my initial proposal (having defaults in for loops) with respect to readability. Regards, George Jul 18 '05 #5

 P: n/a On Thu, 24 Mar 2005 12:07:44 -0500, "George Sakkis" wrote: "Kay Schluehr" wrote: [snipped] Wouldn't it be fun to use in Python? Only drawback: does not look like executable pseudo-code anymore :( Regards KayI don't know if it would be fun, but it certainly doesn't look accessible to mere mortals :-) I'mnot sure if the mind boggling is more due to the syntax with the '->' and all or the semantics, butit goes in the oppposite direction from my initial proposal (having defaults in for loops) withrespect to readability.Regards,George Hi George, I think I got the default variable functions working now. if isa('name'): do something Checks locals, globals, and builtins for the varable name. variable = ifno('name', object) Sets a variable name to an object if it does not exist, or sets it to the 'variable name's object if it does. (The target doesn't have to match.) They are slower than try/except, but can be used in expressions. Like: x,y,z = x,y, ifno('z',0) #---Here's the code--------------------- import sys def isa(v): """ Check if a varable exists in the current (parent to this function), global, or builtin name spaces. use: bool = isa( str ) returns True or False """ plocals = sys._getframe(1).f_locals if plocals.has_key(v) or globals().has_key(v) or \ __builtins__.locals().has_key(v): return True return False def ifno(v, obj=None): """ Check if a varable does not exists, return a default value, otherwise return the varable obj. use: obj = ifno( str [,obj=None] ) if str exist, returns str's object if str does not exist, returns specified object """ plocals = sys._getframe(1).f_locals if plocals.has_key(v): return plocals[v] if globals().has_key(v): return globals()[v] if __builtins__.locals().has_key(v): return __builtins__.locals()[v] return obj def test(): """ Test isa() and ifno() functions: """ # Totally useless routine. ;) import random for n in range(25): # Delete a random x,y,z coordinate to # simulate an unrealiabe data source. d = random.choice([1,2,3]) if d==1: if isa('x'): del x elif d==2: if isa('y'): del y else: if isa('z'): del z # Replace the missing Varible with a random number. r = int(random.random()*100) x, y, z = ifno('x',r), ifno('y',r), ifno('z',r) print x, y, z if __name__ == '__main__': test() #------------------------------------- Jul 18 '05 #6

 P: n/a On 24 Mar 2005 01:58:48 -0800, "Kay Schluehr" wrote: I personally don't like using exec and eval for stuff different fromevaluating user input. I lean the other way. I never want to use user impute for eval and exec. Way too risky. But limited use, that is not user input, should be ok. The programmer can do anything he/she wants anyways, so exec or eval is just another tool in that respect. But I am trying to find a way to limit the code objects. To make it safe I need to hide all the name spaces except locals within the function. Not sure if it's possible.. um, not easy. I know it's possible. You rely much on "evaluate statement on the line" by adaptingconventional Python syntax. I think one can go a bit further breakingthe syntactical prejudices and apply tuple-actions :)Playing a bit with tuple-actions shows that the concept is quitepowerfull and can be used to create simple statements.First of all the semantics has to be patched:We have (x,y,z=0) -> (x,y,z)as a tuple assignment ((x,y,z=0)->(x,y,z))(a,b,c) = (x=a,y=b,z=c)But it is not clear what (x,y,z=0) -> x*y-zactually means? I think I'm following you, but I'm not sure how much of it is diagramic or meant to be actual code. Are you suggesting to use the -> as a operator? If so then this is backwards to the = operator. x*y-z <- (x,y,z=0) Would be more consistent to current syntax. Then the <- would have the meaning of translate instead of assign. Or possibly an operator for adapt? Guido wants to use the -> in function definitions to specify return value types. Although I see problems with that too. Proposal: (x,y=0) -> x*y => ((x,y=0)->x*y) (a,b) -> (x=a,y=b),a*b (x,y=0) -> (x*y) => ((x,y=0)->(x*y))(a,b) -> (x=a*b,y=b)So (x,y=0) -> x*y is appending the result to the argument tuple.Remark: this is isomorph to (x,y=0,res=None) -> ((x,y),x*y)but it becomes harder now to identify (x,y,res=None) -> ((x,y),x*y)with x*yProvide a compiler-hint: (x,y,()) -> x*y I'm not following you completely here. It appears your trying to create a system to map different arguments to equations in a indirect way. Now we are ready for a few examples: default value: (i) -> (0) # i = 0inplace increment: (i) -> i+1 # i = i+1conditional expression: (i) -> i<3 # i,res = i,i<3 Lost me again, what is res? simple transformation: (res) -> (res+i**2) # res = res+i**2 Define a While loop as a function:def While( par, cond, change, action): par(None) # create default res = 0 while cond(par): action(res) change(par) return resLet's apply it to some tuple actions:While((i)->(0), (i)->i<3, (i)->(i+1), (res)->(res+i**2))and evaluate While stepwise:1. par(None) <=> (i)->(0)(None) # (i) = (0)2. cond(par) <=> (i)->i<3(0) # (i,c) = (0,True)3. action(res) <=> (res) -> (res+i**2)(0) # (res) = (0)4. change(par) <=> (i)->(i+1)(0) # (i) = (1)5. cond(par) <=> (i)->i<3(1) # (1,c) = (0,True)6. action(res) <=> (res) -> (res+i**2)(0) # (res) = (1)7. change(par) <=> (i)->(i+1)(1) # (i) = (2)5. cond(par) <=> (i)->i<3(2) # (2,c) = (0,True)6. action(res) <=> (res) -> (res+i**2)(1) # (res) = (5)7. change(par) <=> (i)->(i+1)(2) # (i) = (3)5. cond(par) <=> (i)->i<3(2) # (2,c) = (0,False)break=> res = 5 If we customize the other control flow primitives For and If it shouldbe possible to create a little language only by using this primitives.It is obvious by definition of our While that we can replace argumentson the fly:conds = [(i)->i<3, (i)->i+2<7, (i)->i>=0][ While((i)->(0), cond, (i)->(i+1), (res)->(res+i**2)) for cond inconds]=> [5,29,0] Wouldn't it be fun to use in Python?Only drawback: does not look like executable pseudo-code anymore :( Regards Kay I think I get the gist of what you are trying to do, but I can't follow it entirely. Well it looks like an interesting puzzle, but you'll need to go a little slower for some of us. I tired though. :) Ron_Adam Jul 18 '05 #7

 P: n/a "Kay Schluehr" wrote: To make my intention clear for another time, also for George who mistrusts these exercises alltogether. I want to derive a syntax and semantics for anonymus functions ( called "tuple-actions" ) that are generalizations of rules that are already used implicitely within Python e.g. tuple-unpacking. This is done by progressive interpretation and extension. They are not there by means of an accident, what Guido claims about the current lambda which he feels to be sticked onto the language. Regards, Kay I'd be glad to see a well-formulated view of the proposed tuple actions, provided I (and suspect others) can get through the syntax burden first. Expressions such as "While((i)->(0), (i)->i<3, (i)->(i+1), (res)->(res+i**2))" do not remind me the python I know and love. Alternatively, if it's not easy to come up with a grokable syntax, I would still be ok with it if it remained "under the hood" for the most part, like other obscure features such as metaclasses and descriptors; for instance I wouldn't mind at all if the traditional control flow primitives were implemented as functions using your idea, as long as I didn't have to know the details (and as long as other things, e.g. execution speed, were the same). As for the forthcoming - as it seems - deprecation or removal of lambdas, I'm in the minority that thinks it's a step in the wrong direction. The way they ended up in the language may be a historic accident, but I would favor a solution that improved them instead of removing them. The notion of 'anonymous functions' alone reveals a misconception in my opinion: that functions *have to* have a name to be 'normal'. Still, a name is just a binding to an object that exists independently of the name. We don't find 'anonymous strings', tuples, lists, etc. bad, so why should functions be different ? In this respect, functions are not 100% first class objects. Instead of removing lambdas, I would prefer to see them upgraded, perhaps with a new syntax, accepting more than one expressions and statements instead of a single expression. Sure, one can define named functions locally to avoid polluting the global scope, but why should he ? Just imagine how would python look like with a similar rule for other primitive types. Anyway, I guess I can live without lambdas but I'll miss them. George Jul 18 '05 #9

 P: n/a On 24 Mar 2005 22:16:10 -0800, "Kay Schluehr" wrote: It's all developed during this discussion. Sometimes I'm a bitsurprised were it goes. I enjoy exploring ideas this way. Many times it leads to dead ends or you just end up with a long way back to where you started, but sometimes you get a surprise, and almost always a deeper understanding of the subject. :) To make my intention clear for another time, also for George whomistrusts these exercises alltogether. I want to derive a syntax andsemantics for anonymus functions ( called "tuple-actions" ) that aregeneralizations of rules that are already used implicitely withinPython e.g. tuple-unpacking. This is done by progressive interpretationand extension. They are not there by means of an accident, what Guidoclaims about the current lambda which he feels to be sticked onto thelanguage. Looking at the syntax of lambda, I think I agree with Guido. result = lambda *args: expression It's works like a function, but is formatted like a for or if statement. It should have been something like this. result = lambda{ *args: expression} Another interesting possibility by exploring ideas and concepts. :) Using a dictionary instead of ()'s to pass the arguments and expressions. This would simplify parsing it, because it could be handled as an an object instead of having to parse the args and expression first. What if you could: x = lambda{ x, y: x+y} Hmm comma creates a problem here. so... x = lambda{ (x,y): x+Y } This is more consistent with python syntax and makes more since. the args are in a tuple as they would be in function. x = lambda{ (x,y): x+y } is same as x = function(x,y): return x+y Could this work too?: x, y, z = lambda{ (x,y): x+y, (x,z):x+z, (x,v):x+v } Short hand for: x,y,z = lambda{(x,y):x+y}, lambda{(x,z):x+z, lambda{(x,v):x+v} For compatibility purposes, You would need to give it a different name: af, afn, ann, lamb, lam, lm, ? Or just call it what it is.. function{(args):expression} Then it would be easy to explain, teach, and remember. Ron_Adam Jul 18 '05 #10

 P: n/a Ron_Adam wrote: What if you could: x = lambda{ x, y: x+y} Hmm comma creates a problem here. so... from __future__ import braces SyntaxError: not a chance Reinhold ;) Jul 18 '05 #11

 P: n/a On Fri, 25 Mar 2005 18:58:27 +0100, Reinhold Birkenfeld wrote: Ron_Adam wrote: What if you could: x = lambda{ x, y: x+y} Hmm comma creates a problem here. so... from __future__ import bracesSyntaxError: not a chanceReinhold ;) LOL, :-) Is that to discourage people from wanting to use them as block designators? Jul 18 '05 #12

 P: n/a Ron_Adam wrote: On Fri, 25 Mar 2005 18:58:27 +0100, Reinhold Birkenfeld wrote:Ron_Adam wrote: What if you could: x = lambda{ x, y: x+y} Hmm comma creates a problem here. so...> from __future__ import bracesSyntaxError: not a chance>Reinhold ;) LOL, :-) Is that to discourage people from wanting to use them as block designators? Well, it certainly expresses one basic design principle behind the language. Together with "import this", I consider this to be the Holy Grail of Python ;) Reinhold Jul 18 '05 #13

### This discussion thread is closed

Replies have been disabled for this discussion.

### Similar topics

Browse more Python Questions on Bytes 