Astuces pour jouer au code en C #


62

Quels conseils généraux avez-vous pour jouer au golf en C #? Je recherche des idées pouvant être appliquées aux problèmes de code de golf en général, qui sont au moins quelque peu spécifiques à C # (par exemple, "supprimer les commentaires" n'est pas une réponse). Merci de poster un pourboire par réponse.

- emprunté à l'idée de marcog;)


BEST TIP => Utilisez quelque chose à côté de .NET si vous ne voulez pas soumettre la réponse la plus longue pour le défi. .NET est conçu pour être très bavard et pour laisser l’EDE faire le typage. Ce qui n’est en fait pas aussi grave que cela en a l'air pour la programmation générale tant que vous avez cette béquille IDE, mais pour le golf de code cette stratégie est certaine d’échouer.
Krowe

Pardonnez-moi la photo d’un calendrier, c’était tout ce que je pouvais trouver à la dernière minute.
undergroundmonorail

Réponses:


59

Au lieu d'utiliser .ToString()use +""pour les nombres et d'autres types pouvant être nativement transtypés en toute sécurité sur une chaîne.

.ToString() <-- 11 chars
+""         <--  3 chars

5
Cela fonctionne aussi dans JS.
Cyoce

1
Il s'agit généralement de 5 caractères si vous devez utiliser la chaîne ultérieurement pour inclure les accolades ...(1+"").DoSomethingWith1String();
TheLethalCoder

1
Si vous avez besoin de la chaîne, vous la stockez habituellement. À peu près n'importe quel autre usage peut en déduire de façon native le ToString () ...
jcolebrand

1
Notez que cela appelle en fait la statique String.Concat(object)avec l'argument, plutôt que l'appel virtuel object.ToString(). Concatconvertit explicitement nullen chaîne vide ( voir la source de référence ). Il n'y a pas de 'casting natif' en cours, vous pouvez convertir n'importe quoi de la sorte, c'est juste que le résultat peut ne pas être très utile dans certains cas! (mais le comportement nul pourrait bien l'être).
VisualMelon

1
Alternatif - interpolation de cordes :$"{n}"
Andriy Tolstoy

41

Une fois, j'ai délibérément placé mon programme namespace Systemafin de pouvoir raccourcir l'accès à une classe spécifique. Comparer

using System;using M=System.Math;

à

namespace System{using M=Math;

9
Rappelez-vous qu'il est préférable de qualifier complètement les classes / fonctions lorsqu'un seul usage résout le problème. Ceci n'est utile que si vous devez appeler quelque chose plus d'une fois, et même alors uniquement pour les éléments de l' Systemespace de noms.
Nick Larsen

Vous pouvez aussi simplement faire using System;class P....
ldam

@Logan: Il ne s'agissait pas simplement using System;mais aussi d'avoir un alias pour une classe dans le même espace de noms, ce qui est plus court que celui que j'ai montré ici.
Joey

C'est encore plus court using static System.Math;en C # 6 (vous pouvez utiliser n'importe laquelle de ces fonctions comme si elles étaient vraiment globales - pas dans une classe). La suggestion initiale peut encore être plus courte que using staticsi vous devez accéder à plusieurs classes.
Lait

@milk: le staticmot-clé supplémentaire est souvent plus long que les économies réalisées en laissant de côté M.les appels de méthode, mais oui, c'est une option, mais il en résulte un coût initial élevé qui nécessite de nombreux appels à amortir.
Joey

30

À utiliser varpour déclarer et initialiser des variables (simples) afin de sauvegarder des caractères sur le type:

string x="abc";

devient

var x="abc";

N'est pas particulièrement nécessaire pour int, bien sûr.


2
N'oubliez pas que varvous ne pouvez pas avoir plusieurs déclarants, par exemple, ce var x="x",y="y";n'est pas possible.
Ian H.

29

Si vous utilisez LINQ, vous pouvez passer directement à une méthode au Selectlieu de créer un lambda.

Donc, au lieu de

foo.Select(x=>int.Parse(x))

vous pouvez utiliser

foo.Select(int.Parse)

directement.

(Découvert récemment en améliorant l'une des réponses C # de Timwi .)


2
FWITW cela s'appelle η-réduction
ThreeFx le

On l'appelle aussi style "Point Free"
Jonathan Wilson

5
Pour plus pragmatique d' entre nous, il est tout simplement plus court : -ème
Joey

23

Rappelez-vous que le plus petit programme compilable en C # comporte 29 caractères:

class P
{
    static void Main()
    {   
    }
}

Commencez donc par retirer cela de votre longueur et déterminez votre réponse en fonction du temps écoulé. C # ne peut pas rivaliser avec d’autres langues en matière d’impression ou de lecture, ce qui est au cœur de la plupart des [code-golf]problèmes, ne vous inquiétez donc pas. En tant que golfeur C #, vous êtes vraiment en concurrence avec la langue.

Quelques autres points à garder à l'esprit:

  • ifSi possible, réduisez toutes les boucles et toutes les instructions en une seule ligne afin de supprimer les crochets.
  • Si vous avez le choix entre stdin et la ligne de commande, utilisez toujours la ligne de commande!

Cela implique généralement aussi ternaire;)
jcolebrand

1
As a C# golfer, you're really competing against the language Incroyablement lié
dorukayhan le

1
En fait, ce n'est pas vrai. Il compile en utilisant static int Main()également, qui serait 28 caractères.
Metoniem


21

Au lieu de

bool a = true;
bool b = false;

faire

var a=0<1;
var b=1<0;

Si vous avez besoin de plusieurs variables, utilisez ceci (suggéré par @VisualMelon )

bool a=0<1,b=!a;

Notez que si vous avez besoin de plusieurs variables du même type, il est généralement plus économique de déclarer le type par une virgulebool a=0<1,b=!a;
Séparez

18

Privilégier l’opérateur ternaire sur if.. les elseblocs le cas échéant.

Par exemple:

if(i<1)
    j=1;
else
    j=0;

est plus efficace:

j=i<1?1:0;

15
Suis-je le seul à penser que le deuxième cas est intrinsèquement plus lisible pour des choses comme celle-ci en général? Je fais ça régulièrement. De plus, si j'ai besoin d'éviter une condition nulle (comme sur une chaîne), je fais quelque chose comme var x = input ?? "";(j'aime mes coalesces)
jcolebrand

Il y a des moments où c'est loin d'être l'option la plus lisible, en particulier lorsqu'il i < 1s'agit d'une déclaration complexe ou lorsque le nom de jest long. OMI, il ne réussit pas non plus à bien transmettre les effets secondaires Dans le cas où if (i < 1)quelque chose comme if (SendEmail(recipient))qui renvoie vrai / faux en fonction du succès des effets secondaires, je préfère la notation if / then.
Nick Larsen le

11
Pas besoin de parenthèses dans le second cas - j=i<1?1:0;suffit.
Danko Durbić

3
La question demande des astuces qui sont quelque peu spécifiques à C #. Ceci est inclus dans les conseils pour toutes les langues .
Peter Taylor

4
@PeterTaylor J'ai répondu à cette question il y a plus de 3 ans, bien avant la création du fil de discussion que vous avez lié
Nellius

15

Utilisation efficace de l'utilisation

Vous pouvez remplacer float (qui est un alias pour System.Single) par zutiliserz=System.Single;

Puis remplacez z=System.Single;par z=Single;en plaçant le programme dans l'espace de noms System. (Comme avec la réponse de Joey)

Ceci peut être appliqué à d'autres types de valeur (utilisez ce pour quoi ils sont un alias), structs et classes


14

Si vous devez utiliser Console.ReadLine()plusieurs fois dans votre code (3 fois au minimum), vous pouvez faire:

Func<string>r=Console.ReadLine;

et puis juste utiliser

r()

au lieu


Je pense que vous devez supprimer ()de la première ligne.
mellamokb

@ Melamokb c'est vrai, merci! fixé.
Cristian Lupascu

1
Tu ne peux pas faire auto r=Console.ReadLine;?
Claudiu

2
@claudiu no, malheureusement pas ideone.com/jFsVPX
Cristian Lupascu

@Claudiu, autoest un C++verbe. varest pour C#. La raison pour laquelle cela ne peut pas être fait est parce qu’elle Console.ReadLineest surchargée et que la signature de la fonction doit être spécifiée afin d’indiquer au compilateur quelle surcharge est souhaitée.
GreatAndPowerfulOz

14

Lors de la lecture de chaque caractère d'un argument de ligne de commande, plutôt que de boucler jusqu'à la longueur de la chaîne:

static void Main(string[]a){
    for(int i=0;i<a[0].Length;)Console.Write(a[0][i++]);
}

Vous pouvez sauvegarder un personnage en utilisant un bloc try / catch pour trouver la fin:

static void Main(string[]a){
    try{for(int i=0;;)Console.Write(a[0][i++]);}catch{}
}

Ceci s'applique à n'importe quel tableau dans un tableau tel que:

  • string[]
  • int[][]
  • IList<IList<T>>

7
C'est vraiment horrible ... j'adore!
Alex Reinking

sainte merde c'est du génie, je viens juste de sauver un personnage en bouclant un tableau
Gaspa79

C'est vraiment diabolique!
GreatAndPowerfulOz

13

Utilisez lambdas pour définir une fonction en C # 6

En C # 6, vous pouvez utiliser un lambda pour définir une fonction:

int s(int a,int b)=>a+b;

C'est plus court que de définir une fonction comme celle-ci:

int s(int a,int b){return a+b;}

3
C # 6 offre une toute nouvelle gamme de capacités en matière de code-golf
jcolebrand

En C # 7, cela peut être fait dans une autre fonction pour créer des fonctions locales. Je doute que cela puisse aider au golf, mais ce n’est encore qu’un truc intéressant à connaître.
TehPers

1
Ce n'est pas formellement un lambda. C'est un membre au corps d'expression .
récursive

13

LINQ

À la place d'utiliser:

Enumerable.Range(0,y).Select(i=>f(i))

pour obtenir un Enumerable avec le résultat de fonction fpour chaque inten que [0,y]vous pouvez utiliser

new int[y].Select((_,i)=>f(i))

si vous avez besoin de stringtout ce qui est implémenté Enumerabledans votre programme, vous pouvez aussi les utiliser

var s="I need this anyway";
s.Select((_,i)=>f(i))

J'utilise cette astuce dans ma réponse au défi de Shamir's Secret Sharing .
aloisdg dit Réintégrer Monica

Je ne pense pas que la partie de chaîne s'exécutera si vous n'itérez pas l'énombrable avec optimisation. Juste échoué pour moi jusqu'à ce que j'ai fait .ToArray () ;. Autre que cela, astuce incroyable!
Gaspa79

Oui, les énumérables sont paresseux, mais cela est vrai pour les trois exemples, pas seulement celui avec la chaîne.
raggy

11

Si vous devez utiliser un générique Dictionary<TKey, TValue>au moins deux fois dans votre code, vous pouvez déclarer une classe de dictionnaire, comme dans cet exemple:

class D:Dictionary<int,string>{}

et puis juste utiliser

D d=new D{{1,"something"},{2,"something else"}};

au lieu de répéter Dictionary<int,string>pour chaque instanciation.

J'ai utilisé cette technique dans cette réponse


2
Et aussi "D d" au lieu de "var d"
Zukki

@Zukki évidemment! À quoi je pensais? :)
Cristian Lupascu

1
Alternative:using D = System.Collections.Generic.Dictionary<int,string>;
Andriy Tolstoy le

10

Vous pouvez utiliser floatet doublelittéraux pour sauver quelques octets.

var x=2.0;
var y=2d;         // saves 1 byte

Lorsque vous avez besoin d’ intarithmétique pour renvoyer un floatou doublevous pouvez utiliser les littéraux pour forcer la conversion.

((float)a+b)/2;  // this is no good
(a+b)/2.0;       // better
(a+b)/2f;        // best      

Si vous rencontrez un jour une situation dans laquelle vous devez effectuer un transtypage, vous pouvez économiser quelques octets en utilisant la multiplication.

((double)x-y)/(x*y);
(x*1d-y)/(x*y);      // saves 5 bytes

Encore plus court:(x-y)*1d/x/y;
récursif

9

Rappelez-vous les endroits où le privé ou le public sont inhérents, tels que:

class Default{static void Main()

par rapport à

public class Default { public static void Main()

5
Et faites toujours une seule lettre en classe :-)
Joey, le

2
Oh, et une autre bonne chose, implicite ici: Mainn'a pas besoin d'arguments contrairement à Java, par exemple.
Joey le

@ Joey: et il n'a pas non plus besoin d'être public.
R. Martinho Fernandes le

1
@martinho ~ avez-vous lu ma réponse? ;) pas de public sur la page principale
jcolebrand le

@Joey ~ J'essayais de le garder à un par poste;) ... imaginé que quelqu'un d'autre publierait un message à propos de la classe principale ou des classes ne constituant qu'une seule lettre. Voyant comment personne d'autre ne l'a fait, je vais y aller et ajouter celui-là aussi.
Jcolebrand

9

Pour les expressions lambda sur une ligne, vous pouvez ignorer les crochets et le point-virgule. Pour les expressions à un paramètre, vous pouvez ignorer les parenthèses.

Au lieu de

SomeCall((x)=>{DoSomething();});

Utilisation

SomeCall(x=>DoSomething);

11
Je n'écris jamais les parenthèses pour les lambdas à un paramètre, même dans le code de production.
R. Martinho Fernandes

J'utilise toujours les crochets car j'aime diviser le lambda en plusieurs lignes pour en améliorer la lisibilité.
Juliana Peña le

3
SomeCall(DoSomething)is better better
GreatAndPowerfulOz

9

En boucle:

Déclarations de variables:

int max;
for(int i=1;i<max;i++){
}

devenir:

int max,i=1;
for(;i<max;i++){
}

Et si vous avez besoin de ou travaillez avec la variable i une seule fois, vous pouvez commencer à -1 (ou 0 en fonction de la situation de la boucle) et incrémenter en ligne:

int max,i=1;
for(;i<max;i++){
  Console.WriteLine(i);
}

à

int max,i=1;
for(;i<max;){
  Console.WriteLine(++i);
}

Et cela réduit d'un caractère et obscurcit également légèrement le code. Faites seulement cela avec la iréférence FIRST , comme ceci : (accordé à un caractère, les optimisations ne sont pas importantes, mais elles peuvent aider)

int max,i=1;
for(;i<max;i++){
  Console.WriteLine(i + " " + i);
}

à

int max,i=1;
for(;i<max;){
  Console.WriteLine(++i + " " + i);
}

quand la boucle n'a pas à s'incrémenter i(boucle dans l'ordre inverse):

for(int i=MAX;--i>0;){
      Console.WriteLine(i);
}

J'ai l'habitude de mettre le ++dans de tels cas directement dans l'en-tête de la boucle: for(;++i<max;)ce qui est à la fois plus facile à suivre et plus difficile à obtenir.
Joey

@ Joey Dans ces cas, j'ai tendance à passer à while (++ i <max) qui a la même longueur mais est plus facile à lire.
ICR

ICR: dépend si vous pouvez également insérer une autre instruction (antérieure) dans l'en- fortête, ce qui sauvegardera à nouveau un caractère.
Joey

Vous pouvez remettre les deux déclarations dans la clause for pour une économie de 1 octet.
récursive

J'aime changer for(;i<max;)pour while(i<max). Même nombre d'octets, mais pour moi, ça a l'air plus propre.
Ayb4btu

8

Dans certaines circonstances, un paramètre de sortie peut enregistrer des caractères. Voici un exemple légèrement artificiel, un algorithme de score de quilles à 10 quilles.

Avec une déclaration de retour:

........10........20........30........40........50........60........70........80........90.......100.......110.......120.......130.......140.......150..
public double c(int[]b){int n,v,i=0,X=10;double t=0;while(i<19){n=b[i]+b[i+1];v=b[i+2];t+=(n<X)?n:X+v;if(b[i]>9)t+=b[i+(i>16|v!=X?3:4)];i+=2;}return t;}

Et avec un paramètre de sortie:

........10........20........30........40........50........60........70........80........90.......100.......110.......120.......130.......140.......
public void d(int[]b,out double t){int n,v,i=0,X=10;t=0;while(i<19){n=b[i]+b[i+1];v=b[i+2];t+=(n<X)?n:X+v;if(b[i]>9)t+=b[i+(i>16|v!=X?3:4)];i+=2;}}

Le paramètre de sortie enregistre ici un total de 5 caractères.


8

En C #, nous n'avons pas le droit if(n%2)de vérifier si nest un nombre pair. Si nous le faisons, nous obtenons un cannot implicity convert int to bool. Une manipulation naïve serait à faire:

if(n%2==0)

Une meilleure façon consiste à utiliser:

if(n%2<1)

J'ai utilisé cela pour gagner un octet ici .

notez que cela ne fonctionne que pour les nombres positifs, car -1%2==-1cela est considéré même avec cette méthode.


6

Interpolation de chaîne

L'interpolation est une amélioration très simple et peu encombrante. Au lieu de:

string.Format("The value is ({0})", (method >> 4) + 8)

il suffit d'utiliser $des expressions en ligne:

$"The value is ({(method >> 4) + 8})"

Ceci, combiné aux nouveaux corps d'expression en C # 6.0, devrait permettre à tout défi de calcul de chaîne simple de devenir golfable en C #.


3
Notez également que i+$" bottles of beer";est plus court que $"{i} bottles of beer".
aloisdg dit Réintégrer Monica

1
@ aloisdg Dans ce premier cas, vous devriez toutefois laisser de $côté.
Metoniem

@ Metoniem En effet! Je l'ai laissé parce que, dans mon cas initial, j'avais deux {i}un devant et un au milieu;)
aloisdg dit Réintégrer Monica

@ aloisdg Ahh, je vois. Ouais, les commentaires honteux ne peuvent pas être édités :(
Metoniem

6

Utilisez C # lambda. Puisque PPCG autorise lambda pour les entrées / sorties, nous devrions les utiliser.

Une méthode C # classique ressemble à ceci:

bool Has(string s, char c)
{
    return s.Contains(c);
}

En tant que lambda, nous écrirons

Func<string, char, bool> Has = (s, c) => s.Contains(c);

Les lambda anonymes sont aussi autorisés:

(s, c) => s.Contains(c)

Enlevez tout le bruit et concentrez-vous!

Mise à jour:

Nous pouvons améliorer un pas de plus avec taitement comme @TheLethalCoder commentaire:

s => c => s.Contains(c);

Exemple de mise en circulation par @Felix Palmen: Comment calculer la clé WPA?

Ce sera utile lorsque vous avez exactement 2 paramètres, alors une variable vide non utilisée _sera meilleure. Voir meta post à ce sujet . J'utilise cette astuce ici . Vous devrez changer un peu la fonction. Exemple: Essayez-le en ligne!


1
Pas sûr que ce soit ailleurs dans les astuces, mais pour cet exemple, vous pouvez aussi utiliser le curry ...s=>c=>...
TheLethalCoder

@TheLethalCoder En effet, nous pouvons! Je mettrai à jour la réponse merci!
aloisdg dit: Réintégrer Monica le

Pouvez-vous utiliser eta-réduction dans ce cas? Quelque chose comme ceci: s=>s.Contains.
corvus_192

Notez que les réponses en C # et en Java de la variété 'lambda non typée' sont en train de perdre de la popularité, vous souhaiterez peut-être participer à la discussion sur ce méta post . L'alternative proposée est:(string s,char c)=>s.Contains(c)
VisualMelon le

Discussion sur les fonctions non
nommées


5

le Compute méthode d'instance de System.Data.DataTable, permet d'évaluer une expression de chaîne simple, par exemple:

C # (compilateur Visual C #) , 166 octets

namespace System.Data
{
    class P
    {
        static void Main()
        {
            Console.Write(new DataTable().Compute("30*2+50*5/4",""));
        }
    }
}

Essayez-le en ligne!

Pas très "golfy" en soi, mais parfois pourrait être utile.


5

Échange de deux variables

Normalement, pour échanger deux variables, vous devez déclarer une variable temporaire pour stocker la valeur. Cela ressemblerait à quelque chose du genre:

var c=a;a=b;b=c;

C'est 16 octets! Il existe d'autres méthodes d'échange qui sont meilleures.

//Using tuples
(a,b)=(b,a);
//Bitwise xoring 
a=a^b^(b=a);
//Addition and subtraction
a=a+b-(b=a);
//Multiplication and division
a=a*b/(b=a);

Les trois derniers ne fonctionnent que pour les valeurs numériques et, comme souligné par ASCII uniquement, les deux derniers peuvent entraîner une exception ArithmeticOverflow. Tous ces éléments représentent 12 octets, soit une économie de 4 octets par rapport au premier exemple.


Ceci ne s'applique toutefois qu'aux nombres, et même dans ce cas, tout ce qui est autre que les nuplets et xor risque de tomber dans les limites de nombre entier, c'est-à-dire que vous appliquez cela aux nombres entiers. Occasionnellement, d'autres types de nombres rencontreront également des limites
ASCII uniquement

4

L'utilisation de LinqPad vous donnera la possibilité de supprimer tout le temps système car vous pouvez exécuter des instructions directement. (Et cela devrait être tout à fait légal dans codegolf ... Personne ne dit que vous avez besoin d'un fichier .exe)

La sortie est effectuée à l'aide de la .Dump()méthode d'extension.


Support .NetFiddle .Dump();)
aloisdg dit: Réintégrer Monica

4

(Un cas particulier de connaître votre priorité d'opérateur !)

Utilisation % option pour une soustraction étroite (légèrement) limitée. Cela peut vous éviter une paire de parenthèses autour d'une soustraction, le résultat que vous voulez multiplier ou diviser par quelque chose; mais attention, il y a de sérieuses limitations.

Au lieu de

char b='5'; // b is some ASCII input
int a=(b-48)*c; // we want to find the numerical value of b, and multiply it by something ('0'==48)

Considérer

char b='5'; // b is some ASCII input
int a=b%48*c; // only good for ASCII within 48 of '0' (positive only)!

Exemples:

'5'%'0'*2 -> 10
'5'%'0'*-1 -> -5
'5'%'0'/2 -> 2

Je viens tout juste de découvrir cela, et j’ai l’impression que ce sera une chose précieuse à ne pas oublier lors de tout travail futur avec ASCII. (Je joue actuellement au golf quelque part où j'utilise ASCII pour des représentations numériques compactes, mais que je dois multiplier par 1ou -1sur une autre condition, et cette bande de 2 octets rayée)


4

Si vous devez inclure plusieurs usingobjets appartenant tous à la même hiérarchie, il est souvent plus court d'utiliser le plus long en tant que namespace:

using System;
using System.Linq;
//Some code

contre:

namespace System.Linq
{
    //Some code
}

3

Découvert ce soir "dans les tranchées" tout en améliorant un code de golf ... si vous avez une classe pour votre traitement, vous pouvez faire le travail dans le constructeur pour enregistrer en déclarant une méthode.

J'ai découvert cela en réduisant une application console - car il y avait un static void Main() , toutes les fonctions et variables devaient être déclarées statiques. J'ai créé une classe imbriquée avec des fonctions membres et des variables, le travail principal étant effectué dans le constructeur. Cela enregistre également les caractères dans le code d'appel.

p.ex. Classe avec méthode:

class a
{
    public void b()
    {
        new c().d("input");
    }
}
class c
{
    public void d(string e)
    {
        System.Console.Write(e.Replace("in", "out"));
    }
}

Classe avec travail dans le constructeur:

class a
{
    public void b()
    {
        new c("input");
    }
}
class c
{
    public c(string e)
    {
        System.Console.Write(e.Replace("in", "out"));
    }
}

Cet exemple enregistre 9 caractères.



3

Déclarer des chaînes vides / correspondantes ensemble

Si vous devez déclarer plusieurs chaînes vides / correspondantes, vous pouvez enregistrer quelques octets avec les éléments suivants:

string a="";string b="";string c=""; // 36 bytes
var a="";var b="";var c="";          // 27 bytes
string a="",b="",c="";               // 22 bytes
string a="",b=a,c=a;                 // 20 bytes

Malheureusement, var a="",b=a,c=a;c'est illégal, commeimplicitly type variable cannot have multiple declarators


Pouvez-vous faire var a=b=c=""comme en javascript?
corvus_192

@ corvus_192 nope - malheureusement pas.
Erresen
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.