2

Disclaimer: This question is about me and hopefully others understanding Python better. My problem can be solved easily in more than one line, I know that.

Suppose I have two functions f(x), g(x,y) so that I can compute the tuple ( f(x), g(x,f(x)) ) as a function of x. I want to sort a list X by these two keys but computing f(x) is expensive so I want to do it only once per x. My current solution is:

X_s = sorted( X , key =  lambda x: (lambda y: ( y , g(x,y) ) )( f(x) ) )

Can I achieve the same without using two lambda functions?

3
  • 1
    I've tried answer this several times, but I just end up confusing myself. It seems impossible to do what you want with only one lambda. Python doesn't have any memoizing, like Haskell does, so you'll need one lambda to compute f and another to compute h(x) = (f(x), g(x, f(x)) Commented Aug 26, 2015 at 0:03
  • That is just what i thought. Thank you for the honorable try. Commented Aug 26, 2015 at 5:43
  • 1
    @BlueTrin: I will but there's nothing wrong with doing that shortly before the deadline... Commented Sep 2, 2015 at 16:36

3 Answers 3

3
+50

There is not a beautiful one-liner for this. However, there are lots of options for obfuscated one-liners using fewer lambdas.

For example, a single lambda expression could be used like so

X_s = [a, for (b, a) in sorted(zip(map(f, X), X), key=lambda y,x: (y, g(x,y)))]

or without any lambdas

X_s = [x for (_, _, x) in sorted([(y, g(x,y), x) for (x,y) in zip(X, map(f, X))])]

But these will always be ugly. Outside the situation of toying around for fun, you should use a real loop for this problem.

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

1 Comment

I'm going with yours because you had a solution without lambda as well :)
2

I love puzzles, so I gave this a try :) I managed to solve the question under the given conditions, but only in a very ugly way:

X_s = sorted(X, key=lambda x: [(f0, g(x, f0)) for f0 in [f(x)]][0])

So, it's definitely possible to do this in one line with one lambda function, but I had to replace the second lambda with a list comprehension.

3 Comments

Well, it competes in ugliness with my own solution with two lambdas but it's a candidate. It is similar to my hint in the description. I wonder why it has to be that ugly because obviously it is not impossible to avoid multiple evaluations of f(x). It is about storing a value on the fly for the next calculation but since lambdas cannot have more than one line i wonder if there is a more elegant construction to overcome this.
@FitzgeraldCreen: The elegant way is to not bend over backwards to do it in one line.
Have a look at the disclaimer.
1

Here's my proposal using generators (kind of verbose, but should do the trick):

from operator import itemgetter

X_s = sorted(((f, g(x,f)) for x,f in ((x,f(x)) for x in X)), key=itemgetter(0,1))

Btw, I realize that's 2 lines, but the first one just imports a standard module to make the sorting on multiple keys more readable (I prefer it over lambdas).

Edit:

Just realize that my original answer doesn't quite give the output you asked for. This should do it (albeit even more verbose):

X_s = [y[2] for y in sorted(((f,g(x,f),x) for x,f in ((x,f(x)) for x in X)), key=itemgetter(0,1))]

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.