Dicas para golfe de código em C #

62

Que dicas gerais você tem para jogar golfe em C #? Estou procurando idéias que possam ser aplicadas para codificar problemas de golfe em geral que sejam pelo menos um pouco específicos para C # (por exemplo, "remover comentários" não é uma resposta). Poste uma dica por resposta.

- emprestado da ideia de marcog;)

jcolebrand
fonte
MELHOR DICA => Use algo ao lado do .NET se não desejar enviar a resposta mais longa para o desafio. O .NET foi projetado para ser muito detalhado e permitir que o IDE faça a digitação. O que na verdade não é tão ruim quanto parece para a programação geral, desde que você tenha essa muleta IDE, mas para o código de golfe essa estratégia certamente falha.
Krille
Perdoe-me pela imagem de um calendário, foi tudo o que pude encontrar em pouco tempo.
Undergroundmonorail

Respostas:

59

Em vez de usar o .ToString()uso +""de números e outros tipos que podem ser convertidos nativamente para uma sequência com segurança.

.ToString() <-- 11 chars
+""         <--  3 chars
jcolebrand
fonte
5
Isso também funciona em JS.
Cyoce
11
Isso geralmente é verdade, 5 caracteres se você precisar usar a corda depois para incluindo as chaves ...(1+"").DoSomethingWith1String();
TheLethalCoder
11
Se você precisar da string, normalmente a está armazenando. Apenas sobre qualquer outro uso pode nativamente inferir o ToString () ...
jcolebrand
11
Observe que isso realmente chama a estática String.Concat(object)com o argumento, em vez da chamada virtual object.ToString(). Concatconverte explicitamente nullpara a cadeia vazia ( consulte a fonte de referência ). Não existe um 'casting nativo', você pode converter qualquer coisa assim, apenas o resultado pode não ser muito útil em alguns casos! (mas o comportamento nulo pode muito bem ser).
VisualMelon
11
Alternativa - interpolação de cordas :$"{n}"
Andriy Tolstoy
41

Certa vez, eu coloquei meu programa deliberadamente namespace Systempara reduzir o acesso a uma classe específica. Comparar

using System;using M=System.Math;

para

namespace System{using M=Math;
Joey
fonte
9
Lembre-se de que é melhor qualificar totalmente as classes / funções quando um único uso resolve o problema. Isso é útil apenas se você precisar chamar algo mais de uma vez e, mesmo assim, apenas para itens no Systemespaço para nome.
Nick Larsen
Você também pode fazer using System;class P....
5139 ldam
@Logan: Não se tratava apenas de using System;ter um alias para uma classe no mesmo espaço para nome, que é mais curto do que mostrei aqui.
Joey
É ainda mais curto using static System.Math;no C # 6 (você pode usar qualquer uma dessas funções como se fossem verdadeiramente globais - não em uma classe). A sugestão original ainda pode ser mais curta do que using staticse você precisar acessar várias classes.
milk
@ leite: a staticpalavra-chave adicional geralmente é mais longa do que qualquer economia de deixar de lado M.as chamadas de método, mas sim, é uma opção, mas tem um alto custo inicial que precisa de muitas chamadas para amortizar.
Joey
30

Use varpara declarar e inicializar variáveis ​​(únicas) para salvar caracteres no tipo:

string x="abc";

torna-se

var x="abc";

Não é particularmente necessário int, é claro.

Joey
fonte
2
Lembre-se de que varnão pode ter vários declaradores, por exemplo, var x="x",y="y";não é possível.
Ian H.
29

Se você estiver usando o LINQ, poderá passar um método diretamente para, em Selectvez de criar um lambda.

Então, ao invés de

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

você pode usar

foo.Select(int.Parse)

diretamente.

(Descoberto recentemente ao melhorar uma das respostas C # de Timwi .)

Joey
fonte
2
FWITW isso é chamado de redução-η
ThreeFx 13/16
É também conhecido como "ponto de acesso gratuito" estilo
Jonathan Wilson
5
Para os mais pragmáticos de nós, é simplesmente mais curto : -þ
Joey
23

Lembre-se de que o menor programa compilável em C # tem 29 caracteres:

class P
{
    static void Main()
    {   
    }
}

Portanto, comece removendo isso do seu comprimento e julgue sua resposta sobre quanto isso leva. O C # não pode competir com outros idiomas quando se trata de imprimir ou ler entradas, que é o coração da maioria dos [code-golf]problemas; portanto, não se preocupe. Como jogador de golfe em C #, você está realmente competindo contra o idioma.

Algumas outras coisas a ter em mente:

  • Reduza todos os loops e ifinstruções para uma única linha, se possível, a fim de remover os colchetes.
  • Se for dada a opção entre stdin e linha de comando, use sempre a linha de comando!
Nick Larsen
fonte
Isso geralmente envolvem ternário bem;)
jcolebrand
11
As a C# golfer, you're really competing against the language Inacreditavelmente relacionado
dorukayhan
11
Na verdade, isso não é verdade. Ele compila usando static int Main()também, que teria 28 caracteres.
Metoniem 14/02
11
@ Metoniem Isso requer returnalgo .
user202729
21

Ao invés de

bool a = true;
bool b = false;

Faz

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

Se você precisar de várias variáveis, use isso (sugerido por @VisualMelon )

bool a=0<1,b=!a;
Yytsi
fonte
Observe que se você precisar de várias variáveis ​​do mesmo tipo, geralmente é mais barato declarar o tipo uma vírgula separar as declaraçõesbool a=0<1,b=!a;
VisualMelon
18

Favorecer o operador ternário sobre if.. elseblocos onde apropriado.

Por exemplo:

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

é mais eficiente:

j=i<1?1:0;
Nellius
fonte
15
Eu sou o único que sente que o segundo caso é inerentemente mais legível para coisas como essa em geral? Eu faço isso rotineiramente. Além disso, se eu preciso para evitar uma condição nula (como em uma corda) eu fazer algo assim var x = input ?? "";(I ama meus aglutina)
jcolebrand
Há momentos em que está longe de ser a opção mais legível, principalmente quando i < 1é uma declaração complexa ou quando o nome de jé longo. Na OMI, também falha em transmitir efeitos colaterais muito bem. No caso de if (i < 1)algo if (SendEmail(recipient))que retorna verdadeiro / falso, dependendo do sucesso dos efeitos colaterais, prefiro a notação if / then.
Nick Larsen
11
Não há necessidade de parênteses no segundo caso - j=i<1?1:0;é suficiente.
Danko Durbić
3
A pergunta pede dicas que são um pouco específicas para C #. Essa é uma das dicas para todos os idiomas .
Peter Taylor
4
@PeterTaylor eu respondi a essa pergunta durante 3 anos atrás, bem antes de o fio é ligada foi criado
Nellius
15

Uso efetivo do uso

Você pode substituir float (que é um apelido para System.Single) zusandoz=System.Single;

Em seguida, substitua z=System.Single;com z=Single;colocando o programa no espaço de nomes System. (Como na resposta de Joey)

Isso pode ser aplicado a outros tipos de valor (use o que eles são um alias), estruturas e classes

Nathan Cooper
fonte
14

Se você precisar usar Console.ReadLine()várias vezes no seu código (mín. 3 vezes), poderá:

Func<string>r=Console.ReadLine;

e depois é só usar

r()

em vez de

Cristian Lupascu
fonte
Eu acho que você precisa remover ()da primeira linha.
precisa saber é o seguinte
@mellamokb isso mesmo, obrigado! fixo.
Cristian Lupascu
11
Você não pode fazer auto r=Console.ReadLine;?
Claudiu
2
@claudiu não, infelizmente não ideone.com/jFsVPX
Cristian Lupascu
@ Claudiu, autoé um C++verbo. varé para C#. A razão pela qual isso não pode ser feito é porque Console.ReadLineestá sobrecarregada, portanto a assinatura da função precisa ser especificada para informar ao compilador qual sobrecarga é desejada.
GreatAndPowerfulOz 08/08/19
14

Ao ler cada caractere de um argumento de linha de comando, em vez de fazer um loop até o comprimento da string:

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

Você pode salvar um personagem usando um bloco try / catch para encontrar o fim:

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

Isso se aplica a qualquer matriz dentro de uma matriz, como:

  • string[]
  • int[][]
  • IList<IList<T>>
Mão-E-Comida
fonte
7
Isso é realmente horrível ... Adoro!
21780 Alex Reinking
Holy crap isso é genial, eu realmente acabou de salvar um personagem ao loop um array
Gaspa79
Isto é verdadeiramente mau!
GreatAndPowerfulOz
13

Use lambdas para definir uma função em C # 6

No C # 6, você pode usar um lambda para definir uma função:

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

Isso é mais curto do que definir uma função como esta:

int s(int a,int b){return a+b;}
ProgramFOX
fonte
3
O C # 6 oferece toda uma nova gama de habilidades para codificar golfe
jcolebrand
No C # 7, isso pode ser feito dentro de outra função para criar funções locais. Duvido que isso ajude durante o golfe, mas ainda é apenas um truque interessante de se saber.
TehPers
11
Isso não é formalmente um lambda. É um membro corporal de expressão .
recursivo
13

LINQ

Ao invés de usar:

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

para obter um Enumerable com o resultado da função fpara todos os intitens que [0,y]você pode usar

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

se você precisar stringou algo que implemente Enumerableno seu programa, você também pode usá-los

var s="I need this anyway";
s.Select((_,i)=>f(i))
esfarrapado
fonte
Uso esse truque na minha resposta para o desafio do compartilhamento secreto de Shamir .
aloisdg diz Reinstate Monica
Eu não acho que a parte da string será executada se você não iterar o ienumerable com a otimização ativada. Apenas falhei para mim até que eu fiz .ToArray () ;. Fora isso, dica incrível!
Gaspa79
Sim, os enumeráveis ​​são preguiçosos, mas isso é verdade para todos os três exemplos, não apenas aquele com a string.
raggy
11

Se você precisar usar um genérico Dictionary<TKey, TValue>pelo menos duas vezes no seu código, poderá declarar uma classe de dicionário, como neste exemplo:

class D:Dictionary<int,string>{}

e depois é só usar

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

em vez de repetir Dictionary<int,string>para cada instanciação.

Eu usei essa técnica nesta resposta

Cristian Lupascu
fonte
2
E também "D d" em vez de "var d"
Zukki 4/15
@Zukki Obviamente! O que eu estava pensando? :)
Cristian Lupascu
11
Alternativa:using D = System.Collections.Generic.Dictionary<int,string>;
Andriy Tolstoy
10

Você pode usar floate doubleliterais para salvar alguns bytes.

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

Quando você precisar de alguma intaritmética para retornar a floatou doublevocê pode usar os literais para forçar a conversão.

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

Se você se deparar com uma situação em que precisa converter, poderá salvar alguns bytes usando multiplicação.

((double)x-y)/(x*y);
(x*1d-y)/(x*y);      // saves 5 bytes
SLuck49
fonte
Ainda mais curto:(x-y)*1d/x/y;
recursivo
9

Lembre-se de onde o privado ou o público são inerentes, como o seguinte:

class Default{static void Main()

em comparação com

public class Default { public static void Main()
jcolebrand
fonte
5
E sempre fazer a letra classe só :-)
Joey
2
Ah, e outra coisa legal, implícita aqui: Mainnão precisa de argumentos em contraste com Java, por exemplo.
Joey
@ Joey: e nem precisa ser público.
R. Martinho Fernandes
11
@martinho ~ você leu minha resposta? ;) nenhum público no main
jcolebrand 31/01
@ Joey ~ Eu estava tentando mantê-lo como um por post;) ... imaginou que alguém postaria sobre main ou classes sendo apenas uma letra. Vendo como ninguém mais tem, eu vou em frente e adiciono esse também.
precisa saber é o seguinte
9

Para expressões lambda de uma linha, você pode pular os colchetes e o ponto e vírgula. Para expressões de um parâmetro, você pode pular os parênteses.

Ao invés de

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

Usar

SomeCall(x=>DoSomething);
Juliana Peña
fonte
11
Eu nunca escrevo os parênteses para lambdas de um parâmetro, mesmo no código de produção.
R. Martinho Fernandes
Eu sempre uso os colchetes porque gosto de dividir o lambda em várias linhas para facilitar a leitura.
Juliana Peña
3
SomeCall(DoSomething)é ainda melhor
GreatAndPowerfulOz 08/08
9

Looping:

Declarações variáveis:

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

tornar-se:

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

E se você precisar ou trabalhar com a variável i apenas uma vez, poderá começar com -1 (ou 0, dependendo da circunstância do loop) e aumentar inline:

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

para

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

E isso reduz em um caractere e ofusca um pouco o código também. Faça isso apenas com a PRIMEIRA ireferência, da seguinte forma: (as otimizações concedidas a um personagem não são muito, mas podem ajudar)

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

para

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

quando o loop não precisar ser incrementado i(loop de ordem inversa):

for(int i=MAX;--i>0;){
      Console.WriteLine(i);
}
jcolebrand
fonte
Eu costumo colocar ++esses casos diretamente no cabeçalho do loop: o for(;++i<max;)que é mais fácil de seguir e mais difícil de errar.
Joey
@ Joey Nesses casos, eu costumo mudar para while (++ i <max), que tem o mesmo comprimento, mas é mais fácil de ler.
ICR
ICR: depende se você também pode colocar outra instrução (anterior) no forcabeçalho, o que salvaria um caractere novamente.
Joey #
Você pode mover as duas declarações novamente para a cláusula for para economia de bytes a1.
recursivo
Eu gosto de mudar for(;i<max;)para while(i<max). Mesmo número de bytes, mas para mim parece mais limpo.
Ayb4btu
8

Há circunstâncias em que um parâmetro de saída pode salvar caracteres. Aqui está um exemplo um pouco artificial, um algoritmo de pontuação de boliche de 10 pinos.

Com uma declaração de retorno:

........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 com um parâmetro de saída:

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

O parâmetro de saída aqui salva um total de 5 caracteres.

M_J_O_N_E_S
fonte
8

Em C #, não temos permissão if(n%2)para verificar se né um número par. Se o fizermos, temos um cannot implicity convert int to bool. Uma manipulação ingênua seria:

if(n%2==0)

Uma maneira melhor é usar:

if(n%2<1)

Eu usei isso para ganhar um byte aqui .

note que isso só funciona para números positivos -1%2==-1, pois é considerado mesmo com esse método.

aloisdg diz Restabelecer Monica
fonte
6

Interpolação de String

Uma melhoria realmente simples de economia de espaço é a interpolação. Ao invés de:

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

use apenas $para incorporar expressões:

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

Isso, junto com os novos corpos de expressão no C # 6.0, deve tornar qualquer desafio simples de cálculo de string bastante rentável no C #.

mınxomaτ
fonte
3
Observe também que i+$" bottles of beer";é menor que $"{i} bottles of beer".
Aloisdg diz Reinstate Monica
11
@aloisdg Nesse primeiro caso, você deve deixar de $fora.
Metoniem 14/02
@Metoniem Indeed! Eu deixá-lo porque no meu caso original que eu tinha dois {i}um na frente e um no meio;)
aloisdg diz Reintegrar Monica
@aloisdg Ahh, entendo. Sim, comentários de vergonha não podem ser editados :(
Metoniem 14/02
6

Use C # lambda. Como o PPCG permite lambda para entrada / saída, devemos usá-los.

Um método clássico de C # se parece com o seguinte:

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

Como lambda, escreveremos

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

Lambda anônima também é permitida:

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

Remova todo o barulho e foque!

Atualizar:

Podemos melhorar mais um passo com o curry, como o comentário do @TheLethalCoder:

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

Exemplo de curvatura de @Felix Palmen: como calcular a chave WPA?

Será útil quando você tiver exatamente 2 parâmetros, então uma variável vazia não utilizada _será melhor. Veja meta post sobre isso . Eu uso esse truque aqui . Você terá que mudar um pouco a função. Exemplo: Experimente online!

aloisdg diz Restabelecer Monica
fonte
11
Não tenho certeza se é em qualquer outro lugar nas pontas, mas para este exemplo, você pode usar currying também ...s=>c=>...
TheLethalCoder
@TheLethalCoder De fato, podemos! Vou atualizar a resposta obrigado!
aloisdg diz Restabelecer Monica
Você pode usar a redução eta neste caso? Algo parecido com isto: s=>s.Contains.
Corvus_192 11/01
Observe que as respostas em C # e Java da variedade 'lambda não tipada' estão caindo em desuso; talvez você queira participar da discussão neste meta post . A alternativa sugerida é(string s,char c)=>s.Contains(c)
VisualMelon
Discussão sobre funções não nomeadas
aloisdg diz Reinstate Monica
5

Faça nomes de classe apenas uma letra. Aprimorando as dicas para golfe de código em C # , vamos de

class Default{static void Main()

para

class D{static void Main()

que bate outros 6 caracteres neste caso.

jcolebrand
fonte
11
idem com nomes de variáveis
Nellius 31/01
11
Como isso é "pelo menos um pouco específico para C #"?
Peter Taylor
5

O Computemétodo de instância de System.Data.DataTable, permite avaliar uma expressão de cadeia simples, por exemplo:

C # (compilador Visual C #) , 166 bytes

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

Experimente online!

Não é muito "golfe" por si só, mas às vezes pode ser útil.

digEmAll
fonte
5

Trocando duas Variáveis

Normalmente, para trocar duas variáveis, você deve declarar uma variável temporária para armazenar o valor. Seria algo parecido com isto:

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

São 16 bytes! Existem alguns outros métodos de troca que são melhores.

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

Os três últimos funcionam apenas para valores numéricos e, como apontado apenas pelo ASCII, os dois últimos podem resultar em uma exceção ArithmeticOverflow. Todos os itens acima são 12 bytes, uma economia de 4 bytes em comparação com o primeiro exemplo.

Modalidade de ignorância
fonte
No entanto, aplica-se apenas a números e, mesmo assim, qualquer coisa que não seja tuplas e xor corre o risco de atingir limites inteiros de você aplicá-lo a números inteiros. Ocasionalmente, outros tipos de números também terão limites
ASCII-only
4

O uso do LinqPad lhe dará a possibilidade de remover toda a sobrecarga do programa, pois você pode executar instruções diretamente. (E deve ser totalmente legal no codegolf ... Ninguém diz que você precisa de um .exe)

A saída é feita usando o .Dump()método de extensão.

EvilFonti
fonte
.NetFiddle support .Dump();)
aloisdg diz Reinstate Monica
4

(Um caso específico de conhecer a precedência do seu operador !)

Use %para subtração limitada (de certa forma) limitada. Isso pode economizar um par de parênteses em torno de uma subtração, cujo resultado você deseja multiplicar ou dividir por algo; mas tenha cuidado, ele tem sérias limitações.

Ao invés 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)!

Exemplos:

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

Acabei de descobrir isso e sinto que será algo importante lembrar sempre que trabalhar com a ASCII no futuro. (No momento, estou jogando golfe em algum lugar em que estou usando ASCII para representações numéricas compactas, mas preciso multiplicar por 1ou com -1base em outra condição e esses 2 bytes distribuídos)

VisualMelon
fonte
4

Se você precisar incluir vários usings que caem da mesma hierarquia, geralmente é mais curto usar o mais longo como namespace:

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

vs:

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

Descoberto hoje à noite "nas trincheiras" enquanto aprimora algum código de golfe ... se você tem uma classe para seu processamento, pode fazer o trabalho no construtor para salvar a declaração de um método.

Descobri isso enquanto reduzia um aplicativo de console - como havia static void Main(), todas as funções e variáveis ​​tinham que ser declaradas estáticas. Criei uma classe aninhada com funções-membro e variáveis, com o trabalho principal realizado no construtor. Isso também salva caracteres no código de chamada.

eg Classe com 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"));
    }
}

Classe com trabalho no construtor:

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

Este exemplo salva 9 caracteres.

M_J_O_N_E_S
fonte
3

Use Actionlike Funcpara definir uma função para uma variável. Actionnão retorna nada ( void), por isso é ótimo para impressão.

Por exemplo:

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

Esta dicas é inspirado @ W0lf grande exemplo de uso de FunccomReadLine .

aloisdg diz Restabelecer Monica
fonte
3

Declarar cadeias vazias / correspondentes juntas

Se você precisar declarar várias cadeias vazias / correspondentes, poderá salvar alguns bytes com o seguinte:

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

Infelizmente var a="",b=a,c=a;é ilegal, poisimplicitly type variable cannot have multiple declarators

Erresen
fonte
Você pode fazer var a=b=c=""como em javascript?
Corvus_192 11/01
@ corvus_192 não - infelizmente não.
Erresen