Suggerimenti per giocare a code-golf in C #


62

Quali consigli generali hai per giocare a golf in C #? Sto cercando idee che possano essere applicate ai problemi del codice golf in generale che siano almeno in qualche modo specifiche per C # (ad esempio "rimuovere i commenti" non è una risposta). Si prega di inviare un suggerimento per risposta.

- preso in prestito dall'idea di Marcog;)


SUGGERIMENTO MIGLIORE => Usa qualcosa oltre a .NET se non vuoi inviare la risposta più lunga alla sfida. .NET è progettato per essere molto dettagliato e per consentire all'IDE di eseguire la digitazione. Il che in realtà non è male come sembra per la programmazione generale fintanto che hai quella stampella IDE ma per il golf del codice quella strategia è certa che fallisca.
Krowe,

Perdonami per la foto di un calendario, è stato tutto ciò che sono riuscito a trovare in breve tempo.
undergroundmonorail,

Risposte:


59

Invece di usare l' .ToString()uso +""per numerici e altri tipi che possono essere lanciati in modo nativo su una stringa in modo sicuro.

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

5
Questo funziona anche in JS.
Cyoce,

1
In genere si tratta in realtà di 5 caratteri se in seguito è necessario utilizzare la stringa per includere le parentesi graffe ...(1+"").DoSomethingWith1String();
TheLethalCoder

1
Se hai bisogno della stringa di solito la stai memorizzando. Quasi ogni altro utilizzo può inferire nativamente ToString () ...
jcolebrand,

1
Si noti che questo in realtà chiama la statica String.Concat(object)con l'argomento, piuttosto che la chiamata virtuale object.ToString(). Concatconverte esplicitamente nullnella stringa vuota ( vedi l'origine di riferimento ). Non esiste un 'casting nativo', puoi convertire qualcosa del genere, è solo che il risultato potrebbe non essere molto utile in alcuni casi! (ma il comportamento nullo potrebbe benissimo essere).
VisualMelon,

1
Alternativa - interpolazione di stringhe :$"{n}"
Andriy Tolstoy

41

Una volta ho deliberatamente inserito il mio programma in namespace Systemmodo da accorciare l'accesso a una classe specifica. Confrontare

using System;using M=System.Math;

per

namespace System{using M=Math;

9
Ricorda solo che è meglio qualificare completamente le classi / funzioni quando un singolo uso risolve il problema. Ciò è utile solo se devi chiamare qualcosa più di una volta e anche solo per gli elementi nello Systemspazio dei nomi.
Nick Larsen,

Puoi anche solo fare using System;class P....
ldam,

@Logan: Non si trattava solo using System;di avere un alias per una classe nello stesso spazio dei nomi, che è più breve di come ho mostrato qui.
Joey,

È ancora più breve da fare using static System.Math;in C # 6 (aggiungi puoi usare una di quelle funzioni come se fossero veramente globali, non in una classe). Il suggerimento originale potrebbe essere ancora più breve rispetto a using staticquando è necessario accedere a più classi.
latte

@milk: la staticparola chiave aggiuntiva spesso è più lunga di qualsiasi risparmio derivante dall'abbandonare le M.chiamate al metodo, ma sì, è un'opzione, ma ha un costo iniziale elevato che ha bisogno di molte chiamate per ammortizzare.
Joey,

30

Utilizzare varper dichiarare e inizializzare (singole) variabili per salvare caratteri sul tipo:

string x="abc";

diventa

var x="abc";

Non è particolarmente necessario per int, ovviamente.


2
Ricorda che varnon può avere più dichiaratori, ad esempio var x="x",y="y";non è possibile.
Ian H.

29

Se si utilizza LINQ è possibile passare direttamente un metodo Selectinvece di creare una lambda.

Quindi, invece di

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

Puoi usare

foo.Select(int.Parse)

direttamente.

(Scoperto di recente quando si migliora una delle risposte C # di Timwi .)


2
FWITW questo si chiama riduzione η
ThreeFx


5
Per i più pragmatici di noi, è semplicemente più breve : -þ
Joey

23

Ricorda che il più piccolo programma compilabile in C # è di 29 caratteri:

class P
{
    static void Main()
    {   
    }
}

Quindi inizia rimuovendolo dalla tua lunghezza e giudica la tua risposta su quanto ci vuole. C # non può competere con altre lingue quando si tratta di stampare o leggere input, che è il cuore della maggior parte dei [code-golf]problemi, quindi non preoccuparti. Come giocatore di golf C #, stai davvero competendo contro la lingua.

Alcune altre cose da tenere a mente:

  • Ridurre tutti i loop e le ifistruzioni su una singola riga, se possibile, al fine di rimuovere le parentesi.
  • Se viene data l'opzione tra stdin e la riga di comando, usa sempre la riga di comando!

Questo di solito coinvolge anche ternario;)
jcolebrand

1
As a C# golfer, you're really competing against the language Incredibilmente correlato
Dorukayhan,

1
In realtà, non è vero. Si compila anche usando static int Main(), che sarebbe di 28 caratteri.
Metoniem,


21

Invece di

bool a = true;
bool b = false;

fare

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

Se hai bisogno di più variabili, usa questo (suggerito da @VisualMelon )

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

Si noti che se sono necessarie più variabili dello stesso tipo, in genere è più economico dichiarare il tipo una virgola separare le dichiarazionibool a=0<1,b=!a;
VisualMelon

18

Favorire l'operatore ternario su if... elseblocchi ove appropriato.

Per esempio:

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

è più efficiente:

j=i<1?1:0;

15
Sono l'unico a ritenere che il secondo caso sia intrinsecamente più leggibile per cose come questa in generale? Lo faccio regolarmente. Inoltre, se devo evitare una condizione nulla (come su una stringa) faccio qualcosa del tipo var x = input ?? "";(
adoro le

Ci sono momenti in cui è ben lungi dall'essere l'opzione più leggibile, in particolare quando i < 1è un'istruzione complessa o quando il nome di jè lungo. IMO, inoltre, non riesce a trasmettere molto bene gli effetti collaterali. Nel caso in cui if (i < 1)qualcosa del genere if (SendEmail(recipient))ritorni vero / falso a seconda del successo degli effetti collaterali, preferisco la notazione if / then.
Nick Larsen,

11
Non c'è bisogno di parentesi nel secondo caso - j=i<1?1:0;è sufficiente.
Danko Durbić,

3
La domanda richiede suggerimenti che sono in qualche modo specifici per C #. Questo è incluso nei suggerimenti per tutte le lingue .
Peter Taylor,

4
@PeterTaylor Ho risposto a questa domanda più di 3 anni fa, molto prima che venisse creato il thread che hai collegato
Nellius

15

Uso efficace dell'uso

È possibile sostituire float (che è un alias per System.Single) con l' zutilizzoz=System.Single;

Quindi sostituire z=System.Single;con z=Single;posizionando il programma nello spazio dei nomi System. (Come con la risposta di Joey)

Questo può essere applicato per altri tipi di valore (utilizzare ciò per cui sono un alias), strutture e classi


14

Se devi utilizzare Console.ReadLine()più volte nel tuo codice (minimo 3 volte), puoi fare:

Func<string>r=Console.ReadLine;

e poi basta usare

r()

anziché


Penso che devi rimuovere ()dalla prima riga.
mellamokb,

@mellamokb esatto, grazie! fisso.
Cristian Lupascu,

1
Non puoi farlo auto r=Console.ReadLine;?
Claudiu,

2
@claudiu no, purtroppo non ideone.com/jFsVPX
Cristian Lupascu

@Claudiu, autoè un C++verbo. varè per C#. Il motivo per cui non è possibile farlo è perché Console.ReadLineè sovraccarico, quindi è necessario specificare la firma della funzione per comunicare al compilatore quale sovraccarico è desiderato.
GreatAndPowerfulOz

14

Durante la lettura di ciascun carattere di un argomento della riga di comando, anziché eseguire il ciclo fino alla lunghezza della stringa:

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

Puoi salvare un personaggio usando un blocco try / catch per trovare la fine:

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

Questo vale per qualsiasi array all'interno di un array come:

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

7
È davvero orribile ... lo adoro!
Alex Reinking,

questa merda è geniale, in realtà ho appena salvato un personaggio mentre
eseguivo il

Questo è veramente malvagio!
GreatAndPowerfulOz

13

Usa lambdas per definire una funzione in C # 6

In C # 6, puoi usare un lambda per definire una funzione:

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

È più breve della definizione di una funzione come questa:

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

3
C # 6 offre una gamma completamente nuova di abilità nel code-golf
jcolebrand,

In C # 7, questo può essere fatto all'interno di un'altra funzione per creare funzioni locali. Dubito che questo aiuterà durante il golf, ma è ancora solo un trucco da sapere.
TehPers,

1
Non è formalmente un lambda. È un membro con espressione corporea .
ricorsivo il

13

LINQ

Invece di usare:

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

per ottenere un Enumerable con il risultato della funzione fper ogni intin [0,y]è possibile utilizzare

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

se hai bisogno stringo qualcosa che implementa Enumerablenel tuo programma, puoi usarli anche tu

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

Uso questo trucco nella mia risposta per la sfida della condivisione segreta di Shamir .
aloisdg dice Reinstate Monica il

Non penso che la parte di stringa verrà eseguita se non esegui l'iterazione dell'enumerabile con l'ottimizzazione attivata. Ho appena fallito fino a quando non l'ho fatto .ToArray () ;. A parte questo, fantastico consiglio!
Gaspa79,

Sì, gli enumerabili sono pigri, ma questo è vero per tutti e tre gli esempi, non solo quello con la stringa.
raggy

11

Se è necessario utilizzare un generico Dictionary<TKey, TValue>almeno due volte nel codice, è possibile dichiarare una classe del dizionario, come in questo esempio:

class D:Dictionary<int,string>{}

e poi basta usare

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

invece di ripetere Dictionary<int,string>per ogni istanza.

Ho usato questa tecnica in questa risposta


2
E anche "D d" invece di "var d"
Zukki,

@Zukki Ovviamente! Cosa stavo pensando? :)
Cristian Lupascu il

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

10

È possibile utilizzare letterali floate doubleper salvare alcuni byte.

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

Quando hai bisogno di un po 'di intaritmetica per restituire un floatoppure doublepuoi usare i letterali per forzare la conversione.

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

Se ti capita di imbatterti in una situazione in cui devi lanciare, puoi salvare alcuni byte usando invece la moltiplicazione.

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

Ancora più breve:(x-y)*1d/x/y;
ricorsivo

9

Ricorda dove sono inerenti privati ​​o pubblici, come i seguenti:

class Default{static void Main()

paragonato a

public class Default { public static void Main()

5
E fai sempre solo una lettera di classe :-)
Joey,

2
Oh, e un'altra cosa carina, implicita qui: Mainnon ha bisogno di alcun argomento in contrasto con Java, per esempio.
Joey,

@Joey: e nemmeno deve essere pubblico.
R. Martinho Fernandes,

1
@martinho ~ hai letto la mia risposta? ;) nessun pubblico sul main
jcolebrand

@Joey ~ Stavo cercando di tenerlo ad uno per posta;) ... ho pensato che qualcun altro avrebbe postato sul fatto che il main o le classi fossero solo una lettera. Visto che nessun altro ha, vado avanti e aggiungo anche quello.
jcolebrand,

9

Per le espressioni lambda a una riga, puoi saltare le parentesi e il punto e virgola. Per le espressioni con un parametro, è possibile saltare le parentesi.

Invece di

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

Uso

SomeCall(x=>DoSomething);

11
Non scrivo mai le parentesi per lambda a un parametro, anche sul codice di produzione.
R. Martinho Fernandes,

Uso sempre le parentesi perché mi piace dividere la lambda in più righe per leggibilità.
Juliana Peña,

3
SomeCall(DoSomething)è ancora meglio
GreatAndPowerfulOz

9

looping:

Dichiarazioni variabili:

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

diventare:

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

E se hai bisogno o lavori con la variabile i solo una volta, potresti iniziare da -1 (o 0 a seconda della circostanza del ciclo) e incrementare in linea:

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

per

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

E questo si riduce di un carattere e offusca leggermente anche il codice. Fallo solo al PRIMO iriferimento, in questo modo: (a condizione che le ottimizzazioni di un carattere non siano molto, ma possono aiutare)

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

per

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

quando il loop non deve aumentare i(loop di ordine inverso):

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

Di solito inserisco il messaggio ++in questi casi direttamente nell'intestazione del loop: for(;++i<max;)che è sia più facile da seguire sia più difficile sbagliare.
Joey

@Joey In quei casi tendo a passare a while (++ i <max) che è della stessa lunghezza ma più facile da leggere.
ICR

ICR: dipende dal fatto che sia possibile inserire anche un'altra istruzione (precedente) fornell'intestazione, che salverebbe di nuovo un personaggio.
Joey,

È possibile spostare entrambe le dichiarazioni nella clausola for per un risparmio di 1 byte.
ricorsivo il

Mi piace cambiare for(;i<max;)in while(i<max). Stesso numero di byte, ma per me sembra più pulito.
Ayb4btu,

8

Ci sono circostanze in cui un parametro di output può salvare caratteri. Ecco un esempio leggermente inventato, un algoritmo di punteggio bowling a 10 pin.

Con una dichiarazione di ritorno:

........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;}

E con un parametro di output:

........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;}}

Il parametro di output qui salva un totale di 5 caratteri.


8

In C #, non ci è permesso fare if(n%2)per verificare se nè un numero pari. Se lo facciamo, otteniamo un cannot implicity convert int to bool. Una gestione ingenua sarebbe fare:

if(n%2==0)

Un modo migliore è usare:

if(n%2<1)

Ho usato questo per ottenere un byte qui .

nota che questo funziona solo con numeri positivi, poiché -1%2==-1è considerato anche con questo metodo.


6

Interpolazione di stringhe

Un miglioramento salvaspazio davvero semplice è l'interpolazione. Invece di:

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

basta usare $per incorporare espressioni:

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

Questo, insieme ai nuovi corpi di espressione in C # 6.0, dovrebbe rendere una semplice sfida di calcolo delle stringhe abbastanza giocabile in C #.


3
Si noti inoltre che i+$" bottles of beer";è più breve di $"{i} bottles of beer".
aloisdg dice Reinstate Monica l'

1
@aloisdg In quel primo caso, dovresti lasciarlo $fuori.
Metoniem,

@Metoniem Indeed! L'ho lasciato perché nel mio caso originale ne avevo due {i}uno davanti e uno nel mezzo;)
aloisdg dice Reinstate Monica il

@aloisdg Ahh, capisco. Sì, i commenti sulla vergogna non possono essere modificati :(
Metoniem

6

Usa C # lambda. Poiché PPCG consente lambda per input / output, dovremmo usarli.

Un metodo C # classico si presenta così:

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

Come lambda, scriveremo

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

Sono ammessi anche lambda anonimi:

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

Rimuovi tutto il rumore e metti a fuoco!

Aggiornare:

Possiamo migliorare un ulteriore passo con il curry come commento di @TheLethalCoder:

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

Esempio di curring di @Felix Palmen: come calcolare la chiave WPA?

Sarà utile quando hai esattamente 2 parametri, quindi una variabile vuota non utilizzata _sarà migliore. Vedi meta post su questo . Uso questo trucco qui . Dovrai cambiare un po 'la funzione. Esempio: provalo online!


1
Non sono sicuro che sia altrove nei suggerimenti, ma per questo esempio puoi usare anche il curry ...s=>c=>...
TheLethalCoder

@TheLethalCoder In effetti possiamo! Aggiornerò la risposta grazie!
aloisdg dice Reinstate Monica il

Puoi usare la riduzione dell'eta in questo caso? Qualcosa di simile a questo: s=>s.Contains.
corvus_192,

Si noti che le risposte C # e Java della varietà "non tipizzata lambda" stanno perdendo favore, potresti voler unirti alla discussione su questo meta post . L'alternativa suggerita è(string s,char c)=>s.Contains(c)
VisualMelon

Discussione su funzioni non
nominate


5

Il Computemetodo di istanza di System.Data.DataTable, consente di valutare una semplice espressione di stringa, ad esempio:

C # (compilatore Visual C #) , 166 byte

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

Provalo online!

Non molto "golfy" di per sé, ma a volte potrebbe essere utile.


5

Scambiare due variabili

Normalmente, per scambiare due variabili, è necessario dichiarare una variabile temporanea per memorizzare il valore. Sembrerebbe qualcosa del genere:

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

Sono 16 byte! Esistono altri metodi di scambio migliori.

//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);

Gli ultimi tre funzionano solo per valori numerici e, come sottolineato solo da ASCII, gli ultimi due potrebbero comportare un'eccezione ArithmeticOverflow. Tutti i precedenti sono 12 byte, un risparmio di 4 byte rispetto al primo esempio.


Si applica solo per i numeri, e anche in questo caso qualsiasi cosa diversa dalle tuple e dal rischio xor si imbatte in limiti di numero intero di applicarlo a numeri interi. Occasionalmente, anche altri tipi di numeri si imbatteranno in limiti
ASCII, solo il

4

L'uso di LinqPad ti darà la possibilità di rimuovere tutto l'overhead del programma in quanto puoi eseguire direttamente le istruzioni. (E dovrebbe essere completamente legale in codegolf ... Nessuno dice che hai bisogno di un .exe)

L'output viene eseguito utilizzando il .Dump()metodo di estensione.


.NetFiddle support .Dump();)
aloisdg dice Reinstate Monica l'

4

(Un caso particolare di conoscenza della precedenza dell'operatore !)

Utilizzare %per sottrazioni limitate (in qualche modo) limitate. Questo può farti risparmiare una coppia di parentesi attorno a una sottrazione, il cui risultato si desidera moltiplicare o dividere per qualcosa; ma fai attenzione, ha seri limiti.

Invece di

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)

Tener conto di

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

Esempi:

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

L'ho appena scoperto e credo che sarà una cosa preziosa da ricordare ogni volta che lavorerai con ASCII in futuro. (Attualmente sto giocando a golf da qualche parte dove sto usando ASCII per rappresentazioni numeriche compatte, ma ho bisogno di moltiplicare 1o in -1base a un'altra condizione, e questo a strisce 2 byte)


4

Se devi includere più messaggi usingche cadono tutti dalla stessa gerarchia, spesso è più breve usare quello più lungo come namespace:

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

vs:

namespace System.Linq
{
    //Some code
}

3

Scoperto stasera "nelle trincee" migliorando un po 'di codice golf ... se hai una classe per la tua elaborazione, puoi fare il lavoro nel costruttore per salvare dichiarando un metodo.

L'ho scoperto riducendo un'applicazione per console: poiché esisteva un static void Main(), tutte le funzioni e le variabili dovevano essere dichiarate statiche. Ho creato una classe nidificata con funzioni e variabili membro, con il lavoro principale eseguito nel costruttore. Questo salva anche i caratteri nel codice chiamante.

es. classe con metodo:

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

Classe con lavoro nel costruttore:

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

Questo esempio salva 9 caratteri.



3

Dichiarare insieme stringhe vuote / corrispondenti

Se è necessario dichiarare più stringhe vuote / corrispondenti, è possibile salvare alcuni byte con quanto segue:

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

Purtroppo var a="",b=a,c=a;è illegale, comeimplicitly type variable cannot have multiple declarators


Puoi fare var a=b=c=""come in javascript?
corvus_192,

@ corvus_192 no - purtroppo no.
Erresen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.