Consejos para el golf de código en C #

62

¿Qué consejos generales tienes para jugar al golf en C #? Estoy buscando ideas que se puedan aplicar a problemas de golf de código en general que sean al menos algo específicos para C # (por ejemplo, "eliminar comentarios" no es una respuesta). Por favor, publique un consejo por respuesta.

- tomado de la idea de marcog;)

jcolebrand
fuente
MEJOR CONSEJO => Use algo al lado de .NET si no desea enviar la respuesta más larga para el desafío. .NET está diseñado para ser muy detallado y dejar que el IDE escriba. Lo que en realidad no es tan malo como parece para la programación general, siempre y cuando tenga esa muleta IDE, pero para el golf de código esa estrategia es segura.
krowe
Perdóname por la foto de un calendario, fue todo lo que pude encontrar a corto plazo.
undergroundmonorail

Respuestas:

59

En lugar de usar el .ToString()uso +""de números y otros tipos que se pueden convertir de forma nativa en una cadena de forma segura.

.ToString() <-- 11 chars
+""         <--  3 chars
jcolebrand
fuente
55
Esto también funciona en JS.
Cyoce
1
Por lo general, esto es en realidad 5 caracteres si necesita usar la cadena después para incluir las llaves ...(1+"").DoSomethingWith1String();
TheLethalCoder
1
Si necesita la cadena, generalmente la está almacenando. Casi cualquier otro uso puede inferir de forma nativa ToString () ...
jcolebrand
1
Tenga en cuenta que esto realmente llama a la estática String.Concat(object)con el argumento, en lugar de la llamada virtual object.ToString(). Concatse convierte explícitamente nulla la cadena vacía ( vea la fuente de referencia ). No está ocurriendo un 'casting nativo', puedes convertir algo como esto, ¡es solo que el resultado puede no ser muy útil en algunos casos! (pero el comportamiento nulo bien puede ser).
VisualMelon
1
Alternativa - interpolación de cuerdas :$"{n}"
Andriy Tolstoy
41

Una vez coloqué mi programa deliberadamente namespace Systempara poder acortar el acceso a una clase específica. Comparar

using System;using M=System.Math;

a

namespace System{using M=Math;
Joey
fuente
99
Solo recuerde que es mejor calificar completamente las clases / funciones cuando un solo uso resuelve el problema. Esto solo es útil si tiene que llamar a algo más de una vez, e incluso entonces solo para elementos en el Systemespacio de nombres.
Nick Larsen
También puedes simplemente hacer using System;class P....
señora
@Logan: No se trataba solo using System;de tener un alias para una clase en el mismo espacio de nombres, que es más corto de lo que he mostrado aquí.
Joey
Es aún más corto hacerlo using static System.Math;en C # 6 (agregue que puede usar cualquiera de esas funciones como si fueran realmente globales, no en una clase). La sugerencia original aún puede ser más corta que using staticsi necesita acceder a varias clases.
leche
@milk: la staticpalabra clave adicional a menudo es más larga que cualquier ahorro al dejar de lado M.las llamadas al método, pero sí, es una opción, pero tiene un costo inicial considerable que necesita muchas llamadas para amortizarse.
Joey
30

Utilícelo varpara declarar e inicializar variables (individuales) para guardar caracteres en el tipo:

string x="abc";

se convierte

var x="abc";

No es particularmente necesario para int, por supuesto.

Joey
fuente
2
Recuerde que varno puede tener múltiples declaradores, por ejemplo, var x="x",y="y";no es posible.
Ian H.
29

Si usa LINQ, puede pasar un método directamente en Selectlugar de hacer una lambda.

Entonces, en lugar de

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

puedes usar

foo.Select(int.Parse)

directamente.

(Descubierto recientemente al mejorar una de las respuestas C # de Timwi ).

Joey
fuente
2
FWITW esto se llama η-reducción
ThreeFx
También se conoce como estilo "sin puntos"
Jonathan Wilson el
55
Para los más pragmáticos de nosotros, es simplemente más corto : -þ
Joey
23

Recuerde que el programa compilable más pequeño en C # tiene 29 caracteres:

class P
{
    static void Main()
    {   
    }
}

Comience por eliminar eso de su longitud y juzgue su respuesta sobre cuánto más se necesita. C # no puede competir con otros idiomas cuando se trata de imprimir o leer entradas, que es el corazón de la mayoría de los [code-golf]problemas, así que no se preocupe por eso. Como golfista de C #, realmente estás compitiendo contra el lenguaje.

Algunas otras cosas a tener en cuenta:

  • Reduzca todos los bucles y ifdeclaraciones a una sola línea si es posible para eliminar los corchetes.
  • Si se le da la opción entre stdin y línea de comando, ¡use siempre la línea de comando!
Nick Larsen
fuente
Por lo general, esto también implica ternario;)
jcolebrand
1
As a C# golfer, you're really competing against the language Increíblemente relacionado
dorukayhan
1
En realidad, eso no es cierto. Se compila utilizando static int Main()también, que serían 28 caracteres.
Metoniem
1
@Metoniem Eso requiere returnalgo .
user202729
21

En lugar de

bool a = true;
bool b = false;

hacer

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

Si necesita múltiples variables, use esto (sugerido por @VisualMelon )

bool a=0<1,b=!a;
Yytsi
fuente
Tenga en cuenta que si necesita múltiples variables del mismo tipo, generalmente es más barato declarar el tipo una coma separando las declaracionesbool a=0<1,b=!a;
VisualMelon
18

Favorezca al operador ternario sobre if.. elsebloques donde sea apropiado.

Por ejemplo:

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

es más eficientemente:

j=i<1?1:0;
Nellius
fuente
15
¿Soy el único que siente que el segundo caso es inherentemente más legible para cosas como esta en general? Hago eso rutinariamente. Además, si necesito evitar una condición nula (como en una cadena), hago algo como var x = input ?? "";(me encantan mis
uniones
Hay momentos en que está lejos de ser la opción más legible, particularmente cuando i < 1es una declaración compleja o cuando el nombre jes largo. OMI, tampoco logra transmitir los efectos secundarios muy bien. En el caso de que if (i < 1)sea ​​algo como lo if (SendEmail(recipient))que devuelve verdadero / falso dependiendo del éxito de los efectos secundarios, prefiero la notación if / then.
Nick Larsen
11
No hay necesidad de paréntesis en el segundo caso, j=i<1?1:0;es suficiente.
Danko Durbić
3
La pregunta pide consejos que son algo específicos para C #. Este es uno incluido en los consejos para todos los idiomas .
Peter Taylor
44
@PeterTaylor Respondí esta pregunta hace más de 3 años, mucho antes de que se creara el hilo que
vinculaste
15

Uso efectivo del uso

Puede reemplazar float (que es un alias para System.Single) con el zusoz=System.Single;

Luego reemplace z=System.Single;con z=Single;colocando el programa en el espacio de nombres System. (Al igual que con la respuesta de Joey)

Esto se puede aplicar para otros tipos de valores (use para lo que son un alias), estructuras y clases

Nathan Cooper
fuente
14

Si necesita usar Console.ReadLine()varias veces en su código (mínimo 3 veces), puede hacer:

Func<string>r=Console.ReadLine;

y luego solo usa

r()

en lugar

Cristian Lupascu
fuente
Creo que debes eliminarlo ()de la primera línea.
mellamokb
@mellamokb es cierto, gracias! fijo.
Cristian Lupascu
1
¿No puedes hacer auto r=Console.ReadLine;?
Claudiu
2
@claudiu no, desafortunadamente no ideone.com/jFsVPX
Cristian Lupascu
@Claudiu, autoes un C++verbo. vares para C#. La razón por la que esto no se puede hacer es porque Console.ReadLineestá sobrecargado, por lo que la firma de la función debe especificarse para indicarle al compilador qué sobrecarga se desea.
GreatAndPowerfulOz
14

Al leer cada carácter de un argumento de línea de comando, en lugar de recorrer la longitud de la cadena:

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

Puedes guardar un personaje usando un bloque try / catch para encontrar el final:

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

Esto se aplica a cualquier matriz dentro de una matriz como:

  • string[]
  • int[][]
  • IList<IList<T>>
Hand-E-Food
fuente
77
Eso es realmente horrible ... ¡me encanta!
Alex Reinking
Santa mierda, esto es genial, en realidad acabo de guardar un personaje mientras
hago
¡Esto es verdaderamente malvado!
GreatAndPowerfulOz
13

Use lambdas para definir una función en C # 6

En C # 6, puede usar una lambda para definir una función:

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

Esto es más corto que definir una función como esta:

int s(int a,int b){return a+b;}
ProgramFOX
fuente
3
C # 6 da toda una nueva gama de capacidad de código de golf
jcolebrand
En C # 7, esto se puede hacer dentro de otra función para crear funciones locales. Dudo que esto ayude mientras juegas al golf, pero sigue siendo un buen truco para saber.
TehPers
1
Eso no es formalmente una lambda. Es un miembro con expresión corporal .
recursivo el
13

LINQ

En lugar de usar:

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

para obtener una Enumerable con el resultado de la función fpara cada inten el [0,y]que se puede utilizar

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

si necesita stringo cualquier cosa que implemente Enumerableen su programa, también puede usarlos

var s="I need this anyway";
s.Select((_,i)=>f(i))
desigual
fuente
Utilizo este truco en mi respuesta para el desafío Shamir's Secret Sharing .
aloisdg dice Reinstate Monica
No creo que la parte de la cadena se ejecute si no itera lo innumerable con la optimización activada. Simplemente falló para mí hasta que lo hice .ToArray () ;. Aparte de eso, un consejo increíble!
Gaspa79
Sí, los enumerables son vagos, pero eso es cierto para los tres ejemplos, no solo el que tiene la cadena.
desigual
11

Si necesita usar un genérico Dictionary<TKey, TValue>al menos dos veces en su código, puede declarar una clase de diccionario, como en este ejemplo:

class D:Dictionary<int,string>{}

y luego solo usa

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

en lugar de repetir Dictionary<int,string>para cada instanciación.

He usado esta técnica en esta respuesta

Cristian Lupascu
fuente
2
Y también "D d" en lugar de "var d"
Zukki
@Zukki ¡Obviamente! ¿Qué estaba pensando? :)
Cristian Lupascu
1
Alternativa:using D = System.Collections.Generic.Dictionary<int,string>;
Andriy Tolstoi
10

Puede usar floaty doubleliterales para guardar algunos bytes.

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

Cuando se necesita un poco de intaritmética para devolver una floato doublepuede utilizar los literales a forzar la conversión.

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

Si alguna vez te encuentras con una situación en la que tienes que lanzar, puedes guardar algunos bytes usando la multiplicación.

((double)x-y)/(x*y);
(x*1d-y)/(x*y);      // saves 5 bytes
SLuck49
fuente
Aún más corto:(x-y)*1d/x/y;
recursivo
9

Recuerde donde lo privado o lo público son inherentes, como lo siguiente:

class Default{static void Main()

en comparación con

public class Default { public static void Main()
jcolebrand
fuente
55
Y siempre haga que la clase sea solo una letra :-)
Joey
2
Ah, y otra cosa buena, implícita aquí: Mainno necesita ningún argumento en contraste con Java, por ejemplo.
Joey
@Joey: y tampoco necesita ser público.
R. Martinho Fernandes
1
@martinho ~ ¿leíste mi respuesta? ;) no hay público en main
jcolebrand
@Joey ~ Estaba tratando de mantenerlo en uno por publicación;) ... pensé que alguien más publicaría algo acerca de que main o classes solo son una letra. Viendo que nadie más lo ha hecho, seguiré adelante y agregaré ese también.
jcolebrand
9

Para las expresiones lambda de una línea, puede omitir los corchetes y el punto y coma. Para expresiones de un parámetro, puede omitir los paréntesis.

En lugar de

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

Utilizar

SomeCall(x=>DoSomething);
Juliana Peña
fuente
11
Nunca escribo los paréntesis para lambdas de un parámetro, incluso en el código de producción.
R. Martinho Fernandes
Siempre uso los corchetes porque me gusta dividir el lambda en varias líneas para facilitar la lectura.
Juliana Peña
3
SomeCall(DoSomething)es aún mejor
GreatAndPowerfulOz
9

Bucle:

Declaraciones variables:

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

volverse:

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

Y si necesita o trabaja con la variable i solo una vez, puede comenzar en -1 (o 0 según la circunstancia del bucle) e incrementar en línea:

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

a

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

Y eso se reduce en un carácter, y también ofusca un poco el código. Solo haga eso a la PRIMERA ireferencia, así: (las optimizaciones de un personaje otorgadas no son mucho, pero pueden ayudar)

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

a

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

cuando el bucle no tiene que incrementarse i(bucle de orden inverso):

for(int i=MAX;--i>0;){
      Console.WriteLine(i);
}
jcolebrand
fuente
Por lo general, pongo ++estos casos directamente en el encabezado del bucle: for(;++i<max;)que es más fácil de seguir y más difícil de equivocar.
Joey
@Joey En esos casos, tiendo a cambiar a while (++ i <max), que tiene la misma longitud pero es más fácil de leer.
ICR
ICR: depende de si puede poner otra declaración (anterior) en el forencabezado también, lo que luego guardaría un carácter nuevamente.
Joey
Puede mover ambas declaraciones nuevamente a la cláusula for para ahorros de a1 byte.
recursivo el
Me gusta cambiar for(;i<max;)a while(i<max). El mismo número de bytes, pero para mí solo se ve más limpio.
Ayb4btu
8

Hay circunstancias en las que un parámetro de salida puede guardar caracteres. Aquí hay un ejemplo levemente inventado, un algoritmo de puntaje de 10 bolos.

Con una declaración de devolución:

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

Y con un parámetro de salida:

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

El parámetro de salida aquí guarda un total de 5 caracteres.

M_J_O_N_E_S
fuente
8

En C #, no podemos hacer if(n%2)para verificar si nes un número par. Si lo hacemos, obtenemos a cannot implicity convert int to bool. Un manejo ingenuo sería hacer:

if(n%2==0)

Una mejor manera es usar:

if(n%2<1)

Usé esto para ganar un byte aquí .

tenga en cuenta que esto solo funciona para números positivos, ya -1%2==-1que se considera incluso con este método.

aloisdg dice Reinstate Monica
fuente
6

Interpolación de cuerdas

Una mejora realmente simple para ahorrar espacio es la interpolación. En lugar de:

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

solo use $para en línea expresiones:

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

Esto, junto con los nuevos cuerpos de expresión en C # 6.0 debería hacer que cualquier desafío simple de cálculo de cadena sea bastante golfo en C #.

mınxomaτ
fuente
3
También tenga en cuenta que i+$" bottles of beer";es más corto que $"{i} bottles of beer".
aloisdg dice Reinstate Monica
1
@aloisdg Sin embargo, en ese primer caso deberías dejar de $lado.
Metoniem
@Metoniem De hecho! Lo dejé porque en mi caso original tenía dos {i}uno en el frente y uno en el medio;)
aloisdg dice Reinstate Monica
@aloisdg Ahh, ya veo. Sí, los comentarios de vergüenza no se pueden editar :(
Metoniem
6

Use C # lambda. Dado que PPCG permite lambda para entrada / salida, debemos usarlos.

Un método clásico de C # se ve así:

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

Como lambda, escribiremos

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

También se permiten lambda anónimas:

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

¡Elimina todo el ruido y concéntrate!

Actualizar:

Podemos mejorar un paso más con el curry como comentario de @TheLethalCoder:

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

Ejemplo de curring por @Felix Palmen: ¿Cómo calcular la clave WPA?

Será útil cuando tenga exactamente 2 parámetros, entonces una variable vacía no utilizada _será mejor. Ver meta publicación sobre esto . Yo uso este truco aquí . Tendrás que cambiar un poco la función. Ejemplo: ¡ Pruébelo en línea!

aloisdg dice Reinstate Monica
fuente
1
No estoy seguro de si está en otro lugar en los consejos, pero para este ejemplo también puedes usar curry ...s=>c=>...
TheLethalCoder
@TheLethalCoder De hecho, podemos! Actualizaré la respuesta gracias!
aloisdg dice Reinstate Monica
¿Puedes usar eta-reducción en este caso? Algo como esto: s=>s.Contains.
corvus_192
Tenga en cuenta que las respuestas C # y Java de la variedad 'lambda sin tipo' están cayendo en desgracia, es posible que desee unirse a la discusión en esta meta publicación . La alternativa sugerida es(string s,char c)=>s.Contains(c)
VisualMelon
Discusión sobre funciones no relacionadas
aloisdg dice Reinstate Monica
5

Hacer nombres de clase solo una letra. Mejorando los consejos para el golf de código en C # pasamos de

class Default{static void Main()

a

class D{static void Main()

que elimina otros 6 caracteres en este caso.

jcolebrand
fuente
1
lo mismo con los nombres de variables
Nellius
11
¿Cómo es esto "al menos algo específico para C #"?
Peter Taylor
5

El Computemétodo de instancia de System.Data.DataTable, permite evaluar una expresión de cadena simple, por ejemplo:

C # (compilador de Visual C #) , 166 bytes

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

Pruébalo en línea!

No es muy "golf" en sí, pero a veces puede ser útil.

digEmAll
fuente
5

Intercambiando dos variables

Normalmente, para intercambiar dos variables, debe declarar una variable temporal para almacenar el valor. Se vería como algo similar a esto:

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

¡Eso es 16 bytes! Hay algunos otros métodos de intercambio que son mejores.

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

Los últimos tres solo funcionan para valores numéricos, y como se señaló solo en ASCII, los dos últimos podrían resultar en una excepción ArithmeticOverflow. Todo lo anterior tiene 12 bytes, un ahorro de 4 bytes en comparación con el primer ejemplo.

Encarnación de la ignorancia
fuente
Sin embargo, solo se aplica a números, e incluso entonces cualquier cosa que no sea tuplas y xor corra el riesgo de toparse con límites enteros de que está aplicando esto a enteros. Ocasionalmente, otros tipos de números también se encontrarán en límites
solo ASCII
4

El uso de LinqPad le dará la posibilidad de eliminar todos los gastos generales del programa, ya que puede ejecutar instrucciones directamente. (Y debería ser completamente legal en codegolf ... Nadie dice que necesita un .exe)

La salida se realiza utilizando el .Dump()método de extensión.

EvilFonti
fuente
.NetFiddle support .Dump();)
aloisdg dice Reinstate Monica
4

(¡Un caso particular de conocer la precedencia de su operador !)

Úselo %para la sustracción limitada (algo) limitada. Esto puede ahorrarle un par de paréntesis en torno a una resta, cuyo resultado desea multiplicar o dividir por algo; pero ten cuidado, tiene serias limitaciones.

En lugar 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)

Considerar

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

Ejemplos:

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

Acabo de descubrir esto, y siento que será algo valioso para recordar cuando trabaje con ASCII en el futuro. (Actualmente estoy jugando golf en algún lugar donde estoy usando ASCII para representaciones numéricas compactas, pero necesita multiplicar por 1o en -1función de otra condición, y esto se divide en 2 bytes)

VisualMelon
fuente
4

Si necesita incluir múltiples correos usingelectrónicos que caen todos de la misma jerarquía, a menudo es más corto usar el más largo como namespace:

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

vs:

namespace System.Linq
{
    //Some code
}
TheLethalCoder
fuente
3

Descubierto esta noche "en las trincheras" mientras mejora un código de golf ... si tiene una clase para su procesamiento, puede hacer el trabajo en el constructor para guardar la declaración de un método.

Descubrí esto mientras reducía una aplicación de consola, ya que había una static void Main(), todas las funciones y variables tenían que declararse estáticas. Creé una clase anidada con funciones y variables miembro, con el trabajo principal realizado en el constructor. Esto también guarda caracteres en el código de llamada.

Por ejemplo, clase con método:

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

Clase con trabajo en el constructor:

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

Este ejemplo guarda 9 caracteres.

M_J_O_N_E_S
fuente
3

Use Actionlike Funcpara establecer una función en una variable. Actionno devuelve nada ( void), por lo que es ideal para imprimir.

Por ejemplo:

Action<string>w=Console.WriteLine;
w("Hello World");

Estos consejos están inspirados en @ W0lf gran ejemplo de uso de FuncconReadLine .

aloisdg dice Reinstate Monica
fuente
3

Declarar cadenas vacías / coincidentes juntas

Si necesita declarar varias cadenas vacías / coincidentes, puede guardar algunos bytes con lo siguiente:

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

Lamentablemente var a="",b=a,c=a;es ilegal, ya queimplicitly type variable cannot have multiple declarators

Erresen
fuente
¿Se puede hacer me var a=b=c=""gusta en javascript?
corvus_192
@ corvus_192 no, desafortunadamente no.
Erresen