2015-08-25 3 views
2

Avertissement: Cette question concerne moi et j'espère que d'autres personnes comprendront mieux Python. Mon problème peut être résolu facilement en plusieurs lignes, je le sais. Supposons que j'ai deux fonctions f (x), g (x, y) pour que je puisse calculer le tuple (f (x), g (x, f (x))) en fonction de x. Je veux trier une liste X par ces deux clés, mais calculer f (x) est cher donc je ne veux le faire qu'une seule fois par x. Ma solution actuelle est:Python One-Liner: Tri par plusieurs clés interdépendantes

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

Puis-je obtenir la même chose sans utiliser deux fonctions lambda?

+1

J'ai essayé de répondre à cela plusieurs fois, mais je finis par me perdre. Il semble impossible de faire ce que vous voulez avec un seul lambda. Python n'a pas de mémo, comme Haskell, donc vous aurez besoin d'un lambda pour calculer f et un autre pour calculer h (x) = (f (x), g (x, f (x)) – saulspatz

+0

C'est juste ce que je pensais Merci pour l'essai honorable –

+0

vous devriez toujours accepter une réponse – BlueTrin

Répondre

2

J'aime des puzzles, donc je lui ai donné cette solution :) J'ai réussi à résoudre la question dans les conditions données, mais seulement d'une manière très laid:

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

Ainsi, il est certainement possible de le faire dans une ligne avec une fonction lambda, mais j'ai dû remplacer le deuxième lambda par une compréhension de liste.

+0

Eh bien, il rivalise dans la laideur avec ma propre solution avec Je me demande pourquoi il faut que ce soit laid, car il n'est évidemment pas impossible d'éviter plusieurs évaluations de f (x). voler pour le calcul suivant, mais puisque les lambdas ne peuvent pas avoir plus d'une ligne, je me demande s'il y a une construction plus élégante pour surmonter cela –

+0

@FitzgeraldCreen: La façon élégante est de ne pas se pencher en arrière pour le faire en une seule ligne – BrenBarn

+0

Jetez un coup d'œil à l'avertissement. –

1

Voilà ma proposition en utilisant des générateurs (type de bavard, mais devrait faire l'affaire):

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, je me rends compte que ce 2 lignes, mais le premier les importations tout un module standard pour faire le tri sur plusieurs touches plus lisibles (je préfère les lambdas).

Edit:

Il faut juste réaliser que ma première réponse ne donne pas tout à fait la sortie que vous avez demandé. Cela devrait le faire (mais encore plus bavard):

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))] 
3

Il n'y a pas une belle-liner pour cela. Cependant, il y a beaucoup d'options pour les doublures isolées utilisant moins de lambdas.

Par exemple, une seule expression lambda pourrait être utilisé comme si

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

ou sans lambdas

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

Mais ce sera toujours laid. En dehors de la situation de jouer autour pour le plaisir, vous devriez utiliser une véritable boucle pour ce problème.

+0

Je vais avec le vôtre parce que vous avez eu une solution sans lambda aussi bien :) –