107 votes

Permuter deux variables sans utiliser de variable temporaire

J'aimerais pouvoir échanger deux variables sans utiliser de variable temporaire en C#. Est-ce possible ?

decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);

// Swap each:
//   startAngle becomes: 355.87
//   stopAngle becomes: 159.9

36 votes

1 votes

Decimal stopAngle = Convert.ToDecimal(159.9) ; decimal startAngle = Convert.ToDecimal(355.87) ;

1 votes

Je suggère de choisir la réponse fournie par @TimothyP à propos des tuples C# 7 comme nouvelle réponse acceptée. C'est maintenant le moyen le plus pragmatique d'atteindre votre objectif.

235voto

paxdiablo Points 341644

El droite pour échanger deux variables :

decimal tempDecimal = startAngle;
startAngle = stopAngle;
stopAngle = tempDecimal;

En d'autres termes, utiliser une variable temporaire.

Et voilà. Pas d'astuces, pas de mainteneurs de votre code vous maudissant pour les décennies à venir, pas d'entrées à Le WTF quotidien Il n'est pas nécessaire de passer trop de temps à essayer de comprendre pourquoi vous en aviez besoin dans une seule opération, car, au niveau le plus bas, même la fonction la plus compliquée du langage est une série d'opérations simples.

C'est très simple, lisible et facile à comprendre, t = a; a = b; b = t; solution.

À mon avis, les développeurs qui essaient d'utiliser des astuces pour, par exemple, "échanger des variables sans utiliser un temp" ou "le dispositif de Duff" essaient juste de montrer à quel point ils sont intelligents (et échouent misérablement).

Je les compare à ceux qui lisent des livres intellectuels dans le seul but de paraître plus intéressants dans les soirées (au lieu d'élargir leurs horizons).

Les solutions où l'on ajoute et soustrait, ou celles basées sur le XOR, sont moins lisibles et très probablement plus lentes qu'une simple solution de "variable temporaire" (arithmétique/opérations booléennes au lieu de simples déplacements au niveau de l'assemblage).

Rendez service à vous-même et aux autres en écrivant un code lisible et de bonne qualité.

C'est mon coup de gueule. Merci de m'avoir écouté :-)

Par ailleurs, je suis conscient que cela ne répond pas à votre question spécifique (et je m'en excuse), mais il existe de nombreux précédents sur SO où des personnes ont demandé comment faire quelque chose et où la réponse correcte était "Ne le faites pas".

8 votes

+1 ; et pour d'autres raisons : avec les astuces +/- (etc), vous faites de l'arithmétique inutile. Avec les nombres entiers, cela peut être tout à fait acceptable. en un coup d'œil (les arrondis et les débordements ne posent pas de problèmes, et le coût pour le CPU est pratiquement nul), mais avec les décimales, l'addition et la soustraction sont des opérations non triviales. Il ne peut même pas utiliser la FPU, car ce ne sont pas des flottants/doubles. Alors utilisez déjà une variable temporaire ! !!

6 votes

Bien sûr, c'est la meilleure façon, mais il a été explicitement demandé sans variable temp.

0 votes

+1 Je suis d'accord avec vous. Quand on commence à compliquer des choses simples, on se retrouve avec toutes sortes de problèmes à résoudre dans les prochaines années...

135voto

CommuSoft Points 6439

Tout d'abord, échanger sans variable temporaire dans un langage comme C# est une très mauvaise idée .

Mais pour le bien de la réponse, vous pouvez utiliser ce code :

startAngle = startAngle + stopAngle;
stopAngle = startAngle - stopAngle;
startAngle = startAngle - stopAngle;

Des problèmes peuvent toutefois survenir lors de l'arrondi si les deux nombres sont très différents. Cela est dû à la nature des nombres à virgule flottante.

Si vous souhaitez masquer la variable temporaire, vous pouvez utiliser une méthode utilitaire :

public static class Foo {

    public static void Swap<T> (ref T lhs, ref T rhs) {
        T temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
}

42 votes

C'est très bien pour les entiers ou les nombres à virgule fixe. Avec des nombres à virgule flottante, vous obtiendrez de minuscules erreurs d'arrondi. Elles peuvent ou non être assez importantes, cela dépend de la façon dont vous utilisez les nombres.

7 votes

Tant que vous ne rencontrez pas de problèmes de débordement, cela fonctionne très bien.

0 votes

@Kennet Belenky : en effet, mais je ne vois pas d'autre solution, à part une valeur temporaire. Je pense donc que c'est la seule "bonne" façon de résoudre ce problème. (Désolé pour mon anglais, si j'ai fait des erreurs)

74voto

Paul Sonier Points 25528

Oui, utilisez ce code :

stopAngle = Convert.ToDecimal(159.9);
startAngle = Convert.ToDecimal(355.87);

Le problème est plus difficile pour les valeurs arbitraires :-)

43voto

this. __curious_geek Points 23728
int a = 4, b = 6;
a ^= b ^= a ^= b;

Fonctionne pour tous les types, y compris les cordes et les flotteurs.

28 votes

J'espère que l'échange XOR sera oublié un jour.

11 votes

L'échange XOR est proche du sommet de la ringardise. J'ai eu le nirvana pendant quelques jours après l'avoir appris à l'école.

10 votes

Cela ne semble pas fonctionner du tout stackoverflow.com/questions/5577140/

26voto

Marcus Points 61

BenAlabaster a montré une façon pratique de faire un changement de variable, mais la clause try-catch n'est pas nécessaire. Ce code est suffisant.

static void Swap<T>(ref T x, ref T y)
{
     T t = y;
     y = x;
     x = t;
}

L'usage est le même qu'il a montré :

float startAngle = 159.9F
float stopAngle = 355.87F
Swap(ref startAngle, ref stopAngle);

Vous pouvez également utiliser une méthode d'extension :

static class SwapExtension
{
    public static T Swap<T>(this T x, ref T y)
    {
        T t = y;
        y = x;
        return t;
    }
}

Utilisez-le comme ça :

float startAngle = 159.9F;
float stopAngle = 355.87F;
startAngle = startAngle.Swap(ref stopAngle);

Les deux méthodes utilisent une variable temporaire dans la méthode, mais vous n'avez pas besoin de la variable temporaire là où vous effectuez la permutation.

2 votes

Oui, mais seulement dans la méthode, pas à l'endroit où vous faites le changement.

6 votes

Utiliser une abstraction est une bonne façon de résoudre le problème. Elle fournit une solution générale à un problème courant et rend le code d'appel plus facile à lire. Bien sûr, elle utilise quelques octets de mémoire et quelques cycles de processeur supplémentaires, mais à moins que vous n'appeliez ce code des millions de fois, vous ne remarquerez aucune différence.

1 votes

@OlivierJacot-Descombes, j'espère que si vous l'appelez un million de fois, le JIT l'optimisera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X