Multi-line Anonymous Functions in Python

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
whaatt
Posts: 30
Joined: Sun Apr 11, 2010 2:03 pm UTC

Multi-line Anonymous Functions in Python

Postby whaatt » Sun Aug 12, 2012 6:42 am UTC

I was just wondering if there were any implementations of Python that had support for true anonymous functions. RIght now, the lambda construct is only sufficient for simple, singular expression needs (e.g. callbacks), and as far as I know, there are no current plans to include multi-line lambdas in CPython.

In the meantime, I've made a little module (https://github.com/whaatt/Mu) that emulates this functionality, with the caveat that it's not the prettiest thing to look at. Right now, I can do this with the hack, and you can see why I wish there was native support for it:

Code: Select all

f('if a==1:; :return 1; elif a%2 == 0:; :return t(a/2); else:; :return t(3*a+1)')(31) #Result: 1

What I'd like to be able to do is something like this:

Code: Select all

(lambda f(a):
    if a==1:
        return 1
    elif a%2==0:
        return f(a/2)
    else:
        return f(3*a+1))(31)
echo "Dang. Forgot Semicolon."

Giallo
Posts: 226
Joined: Sat Jan 01, 2011 11:31 pm UTC
Location: ETH, Zürich, Switzerland
Contact:

Re: Multi-line Anonymous Functions in Python

Postby Giallo » Tue Aug 21, 2012 10:40 pm UTC

Well, you could use booleans to substitute the if/else, eg:

Code: Select all

f = lambda x: (x >= 3)*2 + (x < 3)*(-1)

should be equivalent to f(x) = 2 if x >= 3, -1 if x < 3.

It is not really nice, but it should work...
"Ich bin ein Teil von jener Kraft, die stets das Böse will und stets das Gute schafft."

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Multi-line Anonymous Functions in Python

Postby troyp » Wed Aug 22, 2012 2:46 am UTC

whaatt wrote:I was just wondering if there were any implementations of Python that had support for true anonymous functions. RIght now, the lambda construct is only sufficient for simple, singular expression needs (e.g. callbacks), and as far as I know, there are no current plans to include multi-line lambdas in CPython.

In the meantime, I've made a little module (https://github.com/whaatt/Mu) that emulates this functionality, with the caveat that it's not the prettiest thing to look at. Right now, I can do this with the hack, and you can see why I wish there was native support for it:

Code: Select all

f('if a==1:; :return 1; elif a%2 == 0:; :return t(a/2); else:; :return t(3*a+1)')(31) #Result: 1

What I'd like to be able to do is something like this:

Code: Select all

(lambda f(a):
    if a==1:
        return 1
    elif a%2==0:
        return f(a/2)
    else:
        return f(3*a+1))(31)


Some general thoughts...
  • I find the lack of proper lambdas in pythons quite irritating (although not as irritating as the lack of say, tail call optimization or goto). Even more annoying is that the lack of lambdas created serious enough problems that Guido was willing to introduce a new construct into the language to fix them and he *still* chose to add a more limited construct rather than fixing lambda (although admittedly decorators do look better with Python's statement-based syntax). Nevertheless, lambdas don't add any power, they just make things a bit easier to read and write sometimes. So I prefer to use named functions rather than string-based hacks to emulate lambda. The latter have their own readability problems (everything's wrapped in strings, which prevents code highlighting) and other issues to boot.
  • In particular, frequent or careless use of exec or eval tends to be a bad idea. They slow things down considerably and could leave a program open to code injection attacks if they are used on any user-generated input (note that this could include something as simple as, say, using the name of a file)
  • If you must use such a hack, you can make things a bit more readable by noting that consecutive strings are concatenated in Python. So your code can be written like this:

    Code: Select all

    f('if a==1:;'
        ' :return 1;'
        'elif a%2 == 0:;'
        ' :return t(a/2);'
        'else:;'
        ' :return t(3*a+1)')(31)

    Also, you can use triple quotes("""...""" or '''...''') to enclose text including newlines, so you could pass a multiline string to your function without needing special syntax and processing. This would be even more readable, since you'd have proper indentation for each line.

    Code: Select all

    f('''
    if a==1:
        return 1
    elif a%2 == 0:
        return t(a/2)
    else:
        return t(3*a+1)
    ''')(31)
  • There's no way to achieve your desired syntax from within standard Python, but you could use, say, a preprocessor to convert multiline lambdas to named functions (you'd need a "gensym" function for generating unique names). This approach wouldn't suffer the problems of an exec-based approach, but *would* require a nonstandard tool. Also, you'd need to make sure your proposed syntax was unambiguous.
  • Just a note on Python names. I was going to check your code, but I changed my mind when I saw it (and by "changed my mind", I mean "reeled back in shock with my hands shielding my eyes" ;-) ). You shouldn't use __this__ form for your own identifiers. Use a trailing underscore (like _this) to indicate a private member, or two (like __this) if you additionally want name mangling (the first just tells programmers (and tools) that the member's private; the second adds a slight disincentive to using it). Keep in mind, though, that you only need to use this for names at the top level or class members. For instance a method parameter, or a loop variable doesn't need underscores in its name. Those variables can't be accessed from outside anyway: they're out of scope.
edit: s/using the len function on a filename/using the name of a file
(Probably not too dangerous to eval an integer, so that wasn't a good example)

User avatar
thoughtfully
Posts: 2243
Joined: Thu Nov 01, 2007 12:25 am UTC
Location: Minneapolis, MN
Contact:

Re: Multi-line Anonymous Functions in Python

Postby thoughtfully » Wed Aug 22, 2012 11:48 am UTC

Giallo wrote:Well, you could use booleans to substitute the if/else, eg:

Code: Select all

f = lambda x: (x >= 3)*2 + (x < 3)*(-1)

should be equivalent to f(x) = 2 if x >= 3, -1 if x < 3.

It is not really nice, but it should work...

That is pretty awful. I can't even tell if it's correct from looking at it.
Python has had a ternary operator like C's ?: since V2.5. The expression would be

Code: Select all

-1 if x<3 else 2

and before that, there were more elegant ways to achieve it. One I used on occasion was to take advantage of the fact that booleans are ints, and use them to index a sequence literal

Code: Select all

(2,-1)[x<3]

which is fairly intuitive, but if the values "2" and "-1" were function calls (or even just attribute lookup) could result in undesired side effects. There are better ways, as can be found in the Python FAQ (and presumably originally from comp.lang.python)

Code: Select all

(x<3 and [-1] or [2])[0]

Which prevents the unused result from being evaluated.
Image
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
-- Antoine de Saint-Exupery

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Multi-line Anonymous Functions in Python

Postby troyp » Wed Aug 22, 2012 4:09 pm UTC

I don't think Giallo's construction is so bad, particularly if it's formatted better. Usually an if-else expression would be better, but on occasion I've used something like Giallo's expression as a kind of cond expression, to represent mathematical definitions for different values of a variable

Code: Select all

f  =  -1  * ( x <  3 ) \
   +   2  * ( x >= 3 )
although I probably wouldn't bother for just two lines (and most of the time I'd just use an if-elif statement anyway).

Even without formatting, it seems more readable than an expression using the condition as an index, despite being more cluttered and verbose. At least the values are paired with their corresponding conditions. In

Code: Select all

(2,-1)[x<3]
you have to use the second value when the condition holds, which is counterintuitive. I suspect you only like it better because you're used to it (or maybe you have a strong preference for concise code).
edit: having criticized this construction, I should admit that I've used something like it as well, although never in this kind of scenario. I've used a boolean variable as an index, but never (afaicr) a literal condition like this. Usually I've done it when the boolean is basically a "flag" discriminating between two states (which I'd think of as state-0 and state-1). So its use as an index would seem quite natural.

As for

Code: Select all

(x<3 and [-1] or [2])[0]
, that is a monstrosity. I'm happy with "and" used as a "guard". And before if-else expressions, the and-or trick, awkward as it was, was okay sometimes, when you knew the expression after "and" would never take a "falsy" value. But trying to fix the construction to work in general is just not worth it. That construction is just as long and cluttered as Giallo's, but harder to read.

User avatar
thoughtfully
Posts: 2243
Joined: Thu Nov 01, 2007 12:25 am UTC
Location: Minneapolis, MN
Contact:

Re: Multi-line Anonymous Functions in Python

Postby thoughtfully » Wed Aug 22, 2012 5:46 pm UTC

Yes, it's not pretty, but it's the only solution, other than the explicit if.. else expression that has the same semantics with respect to side effects as a conventional if/else block. If all you use are literals, fine; otherwise you're going to have trouble staying disciplined using those.
troyp wrote:I suspect you only like it better because you're used to it (or maybe you have a strong preference for concise code).

I didn't claim to like any of them :)

Here, have a concrete example:

Code: Select all

>>> def f():
...  print 'cool'
...  return 'beans'
...
>>> def g():
...  print 'yellow'
...  return 'squash'
...
>>> x=42
>>> if x&10:
...  f()
... else:
...  g()
...
cool
'beans'
>>> f() if x&10 else g()
cool
'beans'
>>> (x&10 and [f()] or [g()])[0]
cool
'beans'
>>> (g(),f())[bool(x&10)]
yellow
cool
'beans'
>>> (not x&10)*g()+(bool(x&10))*f()
yellow
cool
'beans'

That odd construct also fails for any type that hasn't got suitable definitions for addition and multiplication.

Not that I have anything against some concise arithmetic where it makes sense. I've been subtracting a value from the sum to toggle between numeric values about as long as I've been writing code, for instance. On the other hand, that's also one of those cases where it's pretty nearly always literals that are involved.
Image
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
-- Antoine de Saint-Exupery

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Multi-line Anonymous Functions in Python

Postby troyp » Wed Aug 22, 2012 11:32 pm UTC

That's a good point, it's not general. I didn't even consider that issue, I guess 'cause I only ever really use it for mathematical-type definitions where multiplication is defined (and if there are functions in the boolean expressions, they're only pure functions).

It's a shame you can't monkey patch Python builtin classes or you could define multiplication by booleans for all objects. Of course false times an object would presumably yield None, but then you'd have to define addition of None for all objects...which might mask type errors... Actually, that's probably not such a good idea after all (and it still wouldn't allow conditions containing side effects).

User avatar
whaatt
Posts: 30
Joined: Sun Apr 11, 2010 2:03 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby whaatt » Mon Aug 27, 2012 2:56 am UTC

Thanks for all of the feedback everyone.

troyp wrote:Also, you can use triple quotes("""...""" or '''...''') to enclose text including newlines, so you could pass a multiline string to your function without needing special syntax and processing. This would be even more readable, since you'd have proper indentation for each line.

Code: Select all

f('''
if a==1:
    return 1
elif a%2 == 0:
    return t(a/2)
else:
    return t(3*a+1)
''')(31)

This idea never even crossed my mind and I think it's a really cool readability trick.

troyp wrote:Just a note on Python names. I was going to check your code, but I changed my mind when I saw it (and by "changed my mind", I mean "reeled back in shock with my hands shielding my eyes" ;-) ). You shouldn't use __this__ form for your own identifiers. Use a trailing underscore (like _this) to indicate a private member, or two (like __this) if you additionally want name mangling (the first just tells programmers (and tools) that the member's private; the second adds a slight disincentive to using it). Keep in mind, though, that you only need to use this for names at the top level or class members. For instance a method parameter, or a loop variable doesn't need underscores in its name. Those variables can't be accessed from outside anyway: they're out of scope.

I don't blame you in the slightest for reeling back in shock :) . I actually used triple underscores, firstly because there is a chance that somebody might pick a variable name used in the hack's code (which would create a conflict due to the way this works, with eval and all), and secondly if somebody wants to use a variable with _single_, __double__, or __leading type variables when writing an anonymous function.

In anticipation of people wanting to read the code, there's actually another file named "clean.py" with underscores removed. In short, though, I'm using triple underscores because nobody will EVER have a need to use them. Better to err on the side of safety, I figured.

troyp wrote:Nevertheless, lambdas don't add any power, they just make things a bit easier to read and write sometimes.

Exactly my motivation.
echo "Dang. Forgot Semicolon."

aestrivex
Posts: 1
Joined: Tue Dec 11, 2012 5:26 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby aestrivex » Tue Dec 11, 2012 5:38 pm UTC

troyp wrote:Nevertheless, lambdas don't add any power, they just make things a bit easier to read and write sometimes.



without lambdas there is not full support for closures

/usr/lib/some_library.py:

Code: Select all

def give_me_a_function_of_one_argument(k):
  arg = do_some_stuff()
  return k(arg)


/home/user/your_application.py:

give_me_a_function_of_one_argument(close_over_some_stuff(b,c,d,e))

def close_over_something(b,c,d,e):
return lambda a:
do_some_stuff(a,b,c,d,e)



It has nothing to do with the semantics of the language that make it unreasonable to define flow of control statements in such a scenario -- it is literally just the syntax. This can be overcome by other equally unpythonic solutions, but not having support for language constructs in anonymous functions solely because the syntax doesn't look pretty enough cannot be reasonably considered anything but a flaw with the language.

User avatar
phlip
Restorer of Worlds
Posts: 7542
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia
Contact:

Re: Multi-line Anonymous Functions in Python

Postby phlip » Wed Dec 12, 2012 2:50 am UTC

aestrivex wrote:without lambdas there is not full support for closures

Sure there is.

Code: Select all

def outer_function(whatever):
  do_something_with(lambda x:y)
is the same as

Code: Select all

def outer_function(whatever):
  def inner_function(x): return y
  do_something_with(inner_function)
The only difference is that it's not anonymous and is more verbose. It still closes over the outer function's locals in the same way.

aestrivex wrote:not having support for language constructs in anonymous functions solely because the syntax doesn't look pretty enough cannot be reasonably considered anything but a flaw with the language.

I won't disagree with you there, but I will say that while it's a flaw, it's not an easily fixed flaw, but is pretty fundamental with how the language's syntax flows.

Code: Select all

enum ಠ_ಠ {°□°╰=1, °Д°╰, ಠ益ಠ╰};
void ┻━┻︵​╰(ಠ_ಠ ⚠) {exit((int)⚠);}
[he/him/his]

User avatar
Will
There are about a million things I can do from behind
Posts: 2256
Joined: Mon Sep 10, 2007 11:12 pm UTC
Location: St. Heraldwulf's Stone
Contact:

Re: Multi-line Anonymous Functions in Python

Postby Will » Fri Jan 04, 2013 8:23 pm UTC

Guido, at least back when he was in charge, said that there will never be multi-line lambdas; it's impractical given Python's whitespace-based code blocks to implement them in a way that's readable and unambiguous.

IMO, the least bad way to live with the lack of multi-line lambdas is just to accept that you'll have to write a named function most of the time--it's much more readable than trying to cram complex logic into a single expression. Fortunately, it's perfectly allowed to define functions inside other functions and, with the nonlocal keyword in Python 3, Python finally fully supports functional closures, so this really isn't that terrible.
Meaux_Pas: Is it fucking Taint Sunday or something?
liza: Screw y'all, I'm going to the moon

User avatar
Yakk
Poster with most posts but no title.
Posts: 11045
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Multi-line Anonymous Functions in Python

Postby Yakk » Fri Jan 04, 2013 8:33 pm UTC

Use lambas to make conditionally run expressions, look them up with a [], then execute them?

Code: Select all

  x = ((lambda:foo()),(lambda:bar())[condition]()

which I think is valid. [] lookup returns an anonymous 0ary function, which then is executed by the ().
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Multi-line Anonymous Functions in Python

Postby troyp » Sat Jan 05, 2013 3:10 am UTC

Yakk wrote:Use lambas to make conditionally run expressions, look them up with a [], then execute them?

Code: Select all

  x = ((lambda:foo()),(lambda:bar())[condition]()

which I think is valid. [] lookup returns an anonymous 0ary function, which then is executed by the ().

We're talking about multiline lambdas, not inline conditionals, so this is a bit OT, but anyway...

"lambda : foo()" is just the same as "foo", so this would become

Code: Select all

x = (foo, bar)[condition]()
...But I assume foo and bar were meant to be arbitrary expressions and you meant to write

Code: Select all

x = (lambda:foo, lambda:bar)[condition]()
(the parens surrounding the lambdas aren't needed)

This used to be a somewhat common way of doing inline conditionals in Python (although often you wouldn't care about preventing evaluation of the other expression and you'd just simplify to

Code: Select all

x = (foo, bar)[condition]
).


More common was an "and-or trick" using Python's logical operators' "treat everything as a boolean" property:

Code: Select all

x = condition and bar or foo
This wasn't as general, though, since you had to watch for the edge case where bar was "falsy" and itself caused the "and" to fail.

Anyway, Python has had a ternary operator for a while now, so now it's just

Code: Select all

x = bar if condition else foo

User avatar
whaatt
Posts: 30
Joined: Sun Apr 11, 2010 2:03 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby whaatt » Wed Jun 19, 2013 10:35 pm UTC

I hate to revive an old thread, but I recently revisited my efforts at this, and created something that functions effectively as an anonymous function, at least marginally in the style of JavaScript. The code is still at the GitHub repository I linked to in my first post.

Code: Select all

from mu import mu as f
foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]

filteredfoo = filter(f('''
    for i in range(2, round(a**0.5) + 1): #check primality
        if a%i == 0: return False
    return True
''', False), foo)

print([i for i in filteredfoo]) #Result: [2, 17]

I'm not sure how practically useful this would be in the way that anon functions are in JavaScript, but ostensibly they might have some use (syntactical clarity) in applications or modules that use event handlers with lambdas (Twisted is an example, IIRC). What do you all think? Also, what I have here might be a decent compromise in terms of whitespace clarity -- one of the reasons multi-line lambdas were originally shot down -- but I wouldn't know.
echo "Dang. Forgot Semicolon."

EvanED
Posts: 4324
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Multi-line Anonymous Functions in Python

Postby EvanED » Thu Jun 20, 2013 12:34 am UTC

Personally, no way would I ever put code into a string for that reason. Whatever relatively minor syntactic convenience would be achieved by not having an explicit definition of the function would be more than wiped out by the syntactic inconvenience of losing syntax highlighting, autoindentation, etc. within a string; not to mention the other disadvantages (like runtime and lack of compile checking until you try to run the function).

User avatar
whaatt
Posts: 30
Joined: Sun Apr 11, 2010 2:03 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby whaatt » Thu Jun 20, 2013 3:09 am UTC

EvanED wrote:wiped out by the syntactic inconvenience of losing syntax highlighting, autoindentation, etc. within a string; not to mention the other disadvantages (like runtime and lack of compile checking until you try to run the function).

I completely agree, particularly about the syntax highlighting. But this being a proof-of-concept, I think the fact that there are advantages to be gained suggests that maybe for the next major release of Python, GvR and the rest of the dev community should reopen this whole discussion and work out a syntax that is more Pythonic. For now, I suppose defs will suffice, as much as I might gripe about them interrupting my code flow.
echo "Dang. Forgot Semicolon."

User avatar
jestingrabbit
Factoids are just Datas that haven't grown up yet
Posts: 5959
Joined: Tue Nov 28, 2006 9:50 pm UTC
Location: Sydney

Re: Multi-line Anonymous Functions in Python

Postby jestingrabbit » Thu Jun 20, 2013 6:20 pm UTC

But the fact that you can define a named function anywhere the hell you like, that its a valid statement anywhere, makes it unnecessary. I mean, "an anonymous lambda in lined function" vs "a meaningfully named function defined on the lines before you use it", which is more readable? Readability is the heart of what it means to be pythonic.
ameretrifle wrote:Magic space feudalism is therefore a viable idea.

User avatar
whaatt
Posts: 30
Joined: Sun Apr 11, 2010 2:03 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby whaatt » Thu Jun 20, 2013 8:10 pm UTC

jestingrabbit wrote:But the fact that you can define a named function anywhere the hell you like, that its a valid statement anywhere, makes it unnecessary.

I guess. But there's still added namespace clutter, particularly if the named function is used no more than once in the style of a callback or something. For most things, this might not be significant, but in a larger program, maybe something to think about.
echo "Dang. Forgot Semicolon."

User avatar
sparkyb
Posts: 1091
Joined: Thu Sep 06, 2007 7:30 pm UTC
Location: Camberville proper!
Contact:

Re: Multi-line Anonymous Functions in Python

Postby sparkyb » Fri Jun 21, 2013 2:51 am UTC

whaatt wrote:But there's still added namespace clutter, particularly if the named function is used no more than once in the style of a callback or something. For most things, this might not be significant, but in a larger program, maybe something to think about.


The function would be locally scoped so it is no more namespace clutter than local variables.

User avatar
whaatt
Posts: 30
Joined: Sun Apr 11, 2010 2:03 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby whaatt » Fri Jun 21, 2013 6:09 am UTC

sparkyb wrote:
whaatt wrote:But there's still added namespace clutter, particularly if the named function is used no more than once in the style of a callback or something. For most things, this might not be significant, but in a larger program, maybe something to think about.


The function would be locally scoped so it is no more namespace clutter than local variables.

Yeah, I've realized that what I said is a bit of stretch in terms of being a problem. This whole topic is probably a non-issue, and I'm likely just griping since I've been doing a good bit of JavaScript and enjoy the syntactical paradigm. Anyways, thanks everyone for the comments/tips!
echo "Dang. Forgot Semicolon."

User avatar
sparkyb
Posts: 1091
Joined: Thu Sep 06, 2007 7:30 pm UTC
Location: Camberville proper!
Contact:

Re: Multi-line Anonymous Functions in Python

Postby sparkyb » Fri Jun 21, 2013 6:43 pm UTC

It can definitely be hard when switch languages to accept as proper style something that was bad form in the other language and vice versa. JavaScript loves passing around lots of anonymous functions, and that's its paradigm. Not only does Python prefer named locally scoped functions instead, but Python does a lot less passing around of (pseudo-)anonymous functions in the first place. Most functions I pass around as callbacks and such end up being member functions of a class. The only time I can think I've even needed to do this kind of local function thing is when writing my own decorators. Most mapping and filtering stuff I end up doing in-line with generator expressions.

lalop
Posts: 210
Joined: Mon May 23, 2011 5:29 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby lalop » Sun Jul 28, 2013 6:57 am UTC

Is it bad form because the construct doesn't exist, or does the construct not exist because it's bad form?

The reason Guido cited was, IIRC, that he couldn't decide on a good format for multiline lambdas. Aside from that, it could be perfectly ordinary to have them.

I don't care too much because it's only the difference of one def name. But it does result in increased verbosity sometimes, or in cramming into one line what would be less densely expressed in two.

divs1210
Posts: 4
Joined: Sun Jun 25, 2017 12:49 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby divs1210 » Sun Jun 25, 2017 2:00 pm UTC

First define the Y combinator:

Code: Select all

def Y(g):
  exp = lambda f: g(lambda arg: f(f)(arg))
  return (exp)(exp)


This will allow you to have 'named' (recursive) lambdas:

Code: Select all

Y(lambda fact:
    lambda n:
      1 if n < 2 else n * fact(n - 1))(5)
# => 120


So your code becomes:

Code: Select all

Y(lambda f:
    lambda a:
      1 if a == 1  else (f(a/2) if a%2 == 0 else f(3*a + 1)))(31)


Python's ternary conditions are very hard to read, and they can also be replaced with some lambda magic:

Code: Select all

def COND(cond_body_pairs, _else=lambda: None):
  if len(cond_body_pairs) == 0:
    return _else()

  cond, body = cond_body_pairs[:2]
  if cond:
    return body()
  else:
    return COND(cond_body_pairs[2:], _else)


Now your code becomes:

Code: Select all

Y(lambda f:
    lambda a:
      COND((a == 1,
            lambda: 1,

            a%2 == 0,
            lambda: f(a/2)),

           _else =
           lambda: f(3*a + 1)))(31)


which, I would argue, comes pretty close to your ideal of:

Code: Select all

(lambda f(a):
    if a==1:
        return 1
    elif a%2==0:
        return f(a/2)
    else:
        return f(3*a+1))(31)
Last edited by divs1210 on Sun Jun 25, 2017 9:35 pm UTC, edited 1 time in total.

Derek
Posts: 2136
Joined: Wed Aug 18, 2010 4:15 am UTC

Re: Multi-line Anonymous Functions in Python

Postby Derek » Sun Jun 25, 2017 8:02 pm UTC

I feel like you're trying to intentionally invoke Greenspun's Tenth Rule

divs1210
Posts: 4
Joined: Sun Jun 25, 2017 12:49 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby divs1210 » Sun Jun 25, 2017 9:46 pm UTC

Feel free to join in the shenanigans. :P

User avatar
ucim
Posts: 5510
Joined: Fri Sep 28, 2012 3:23 pm UTC
Location: The One True Thread

Re: Multi-line Anonymous Functions in Python

Postby ucim » Mon Jun 26, 2017 3:26 pm UTC

What is the point of an anonymous function? Having written a function, why not give it a name and allow the code to be reused?

Jose
Order of the Sillies, Honoris Causam - bestowed by charlie_grumbles on NP 859 * OTTscar winner: Wordsmith - bestowed by yappobiscuts and the OTT on NP 1832 * Ecclesiastical Calendar of the Order of the Holy Contradiction * Please help addams if you can. She needs all of us.

User avatar
Soupspoon
You have done something you shouldn't. Or are about to.
Posts: 2387
Joined: Thu Jan 28, 2016 7:00 pm UTC
Location: 53-1

Re: Multi-line Anonymous Functions in Python

Postby Soupspoon » Mon Jun 26, 2017 5:21 pm UTC

I haven't dabbled enough in Python to know if we're speaking of the same thing, but in other languages an anonymous function, stored as a reference can be reused interchangably with any other anonymous function.

Say defining a look direction as the "wherever you were looking on an array1 retrieve the {following|previous} element", just basically calling "next item" and letting the particular function currently assigned anonymously do its thing, as would "skip two upwards/downwards" options. The function being so assigned elsewhere and being easily more complicated than merely passing a flag to activate a dumber internal "if flagA then react thus" list.

Or a currying function to ensure no slip-ups in calculating bonuses. You could call multiply(base, multiple) every time, or define bonusify=create_bonusify(multiple) which puts multiply(?,multiple) as a hard-wired reference into bonusify, then you just call bonusify(base1) and bonusify(base2) and so on, knowing that each time the base# is pushed through to join the correct multiple. And, further, if you've set up bonusify_director and bonusify_salesteam and bonusify_cleaner via using the appropriate multiple values for each at the create_bonusify() stage, then you can throw the data around willy-nilly, knowing that (although you could accomplish the same thing by indexed arrays acting globally enough to cover all the necessary subordinate code clauses) you have engineered an auditable method of ensuring consistency of operation.


Ultimately, though, There Is More Than One Way To Do It (and often many different families of methods, never mind distinct coding orders and padding styles) and anonymous functions are answers to various problems. And I wouldn't profess to being an expert in any of this, but I recognise a cool trick when I see one. And then tend to use it to death...


(And if this aint what we're talking about, I might just learn something new and interesting myself, in the proper responses to your question!)

((Unless it's the other answer to the question, which is that if there's no need to reuse a 'packaged' anonymous sub, tjen why clutter up the functional namespace with a name. Perl: sort {$b<=>$a} @list is easy enough without creating a sub for reverse comparisoning and stacking it at the global level (or even in a package, to use) with a hopefully meaningful name probably at least as long as what you just {}ed...))



1 Or a bi-directional, possibly looping, linked list. If you want it to be a more useful method than merely defining an Offset variable to ±1 and pumping that into the persistent location counter...

EvanED
Posts: 4324
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: Multi-line Anonymous Functions in Python

Postby EvanED » Mon Jun 26, 2017 7:06 pm UTC

ucim wrote:What is the point of an anonymous function? Having written a function, why not give it a name and allow the code to be reused?
There are a couple reasons you sometimes want to do that.

The first is that very often, you don't ever need to call it more than once. This is especially true if your function is a "true" closure, i.e. one that captures variables in the surrounding scope. That means that you're not going to be able to refer to it outside of the function it's defined in anyway, which means that you'd need to call it twice within that function.

Then, to define a named function, you have to step "outside of" the expression it's used in. For example, suppose I want to sort a bunch of Person objects by name. You could either say:

Code: Select all

def PersonName(p):
    return p.name

sort(people, key=PersonName);
or

Code: Select all

sort(people, key=lambda p: p.name)

The second is, I think, much clearer -- it puts the emphasis on the actual purpose of the line, which is sorting. The first one is like "hey here's this thing that you don't really care about, oh yeah, we're sorting using it."

(Ironically enough this is an example where it might be useful to put that out next to the Person definition, as that sort might be generally useful. But assume that it was something where you were pretty sure that was the only place that using it would make sense; you run into teh same thing.)

One reason this often applies is that you want a little anonymous function to glue to another, named function. For example, suppose you have def foo(a, b) and want a curried version curry(foo, a)(b). You could write curry as

Code: Select all

def curry(f, a):
    def ____(b):
         return f(a, b)
    return _____
but what do you name the function? I just put _____ because like it's just basically a dummy name; if you were in most languages, basically you'd just be making up a name. For Python, you could introspect on the returned function object and it might be worth doing some more magic in there to give the returned function a nice name. If you don't do that magic though, I think this is slightly cleaner:

Code: Select all

def curry(f, a):
    return lambda b: f(a, b)
because you're not "cluttering" the definition with extra syntax to give the closure a name because the language requires you give it a name. (It's also not defining something that you can reuse...) But you often see the first one, for example in decorators. In fact, I think this is probably the biggest example of when multi-line lambdas would be nice.

divs1210
Posts: 4
Joined: Sun Jun 25, 2017 12:49 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby divs1210 » Tue Jun 27, 2017 8:00 am UTC

What is the point of an anonymous function? Having written a function, why not give it a name and allow the code to be reused?

What is the point of an anonymous integer?
What is the point of an anonymous boolean?
What is the point of an anonymous string?
What is the point of an anonymous object?

Let's name EVERYTHING!

Also, I was able to use lambdas to implement control flow structures above.
Imagine if naming every one of them was a requirement.

Tub
Posts: 300
Joined: Wed Jul 27, 2011 3:13 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby Tub » Tue Jun 27, 2017 1:59 pm UTC

divs1210 wrote:Let's name EVERYTHING!

Alright. Your name is now Bob.

Seriously though, some projects use coding guidelines that prohibit unnamed constants. Instead of

Code: Select all

sleep(3600);

you'd have to write

Code: Select all

const ONE_HOUR = 3600;
sleep(ONE_HOUR);

In other words, there really are projects where you are required to name everything. Not everyone will agree on such guidelines, but there are valid arguments for them.

divs1210
Posts: 4
Joined: Sun Jun 25, 2017 12:49 pm UTC

Re: Multi-line Anonymous Functions in Python

Postby divs1210 » Tue Jun 27, 2017 11:43 pm UTC


wumpus
Posts: 492
Joined: Thu Feb 21, 2008 12:16 am UTC

Re: Multi-line Anonymous Functions in Python

Postby wumpus » Mon Jul 10, 2017 3:53 pm UTC

ucim wrote:What is the point of an anonymous function? Having written a function, why not give it a name and allow the code to be reused?

Jose


It might be 2.x specific, but I've found that [list].sort(key=lambda) to be my main use of lambdas. I *think* it is supposed to be slightly faster than yet another dictionary lookup of my key, but suspect that isn't an issue for pypy. Most of the difference comes down to how far away you are willing to put the definition of your function vs. the use of your function (encapsulation isn't for everything). If you only use it once, there is little reason for a name (also I think some of my sorting lambdas would be shorter than their names).

I'm sure more functional programmers can come up with many good uses for lambdas, I just don't see many in my python (other than sorting, mostly because it wants a function in a parameter: I suspect if you create a function that takes another function, you will be often want to pass a lambda).

Derek
Posts: 2136
Joined: Wed Aug 18, 2010 4:15 am UTC

Re: Multi-line Anonymous Functions in Python

Postby Derek » Tue Jul 11, 2017 10:01 am UTC

divs1210 wrote:Here's the same stuff in Java

Ok, I've got some issues with your Java:

-Since you're using Java 8, prefer Supplier to Calleable. They have the same interface, but Supplier has a more general meaning semantically.
-I don't like the way COND takes a List<Calleable>. This isn't type safe. It should take a List<Pair<Supplier<Boolean>, Supplier<T>>>.
-Don't return null if a COND block is empty. Returning null is generally a bad practice. Throw an exception instead, as COND should never be called with an empty list and has no meaningful return value in this case.
-Don't catch and rethrow exceptions if you don't have to (line 46, for example). The only reason to do this is to wrap a checked exception in a RuntimeException, but the code you're wrapping can't throw checked exceptions in the first place. Not only is the rethrow complicating the code, but it makes the resulting stacktrace harder to read.
-LET should take a List<Pair<String, Object>>.
-I don't like the manual type check and cast for DependentBinding. This is a code smell. I'm not sure what the best solution is right now, but usually this sort of task is solved with some sort of polymorphism.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 9 guests