Советы по коду игры в гольф на C #

62

Какие общие советы у вас есть для игры в гольф в C #? Я ищу идеи, которые могут быть применены к проблемам с гольф-кодом в целом, которые, по крайней мере, несколько специфичны для C # (например, «удалить комментарии» не является ответом). Пожалуйста, оставьте один совет за ответ.

- позаимствовано у идеи Маркога;)

jcolebrand
источник
ЛУЧШИЙ СОВЕТ => Используйте что-то помимо .NET, если вы не хотите предоставлять самый длинный ответ на вызов. .NET спроектирован так, чтобы быть очень многословным и позволять IDE выполнять набор текста. Что на самом деле не так плохо, как звучит для общего программирования, если у вас есть этот IDE костыль, но для кода гольф эта стратегия определенно не удалась.
Кроу
Простите меня за фотографию календаря, это было все, что я смог найти за короткое время.
подземный

Ответы:

59

Вместо использования .ToString()используйте +""для чисел и других типов, которые могут быть безопасно преобразованы в строку.

.ToString() <-- 11 chars
+""         <--  3 chars
jcolebrand
источник
5
Это также работает в JS.
Cyoce
1
Обычно это 5 символов, если вам нужно использовать строку для включения фигурных скобок ...(1+"").DoSomethingWith1String();
TheLethalCoder
1
Если вам нужна строка, вы обычно храните ее.
Практически
1
Обратите внимание, что это фактически вызывает static String.Concat(object)с аргументом, а не виртуальный вызов object.ToString(). Concatявно преобразуется nullв пустую строку ( см. справочный источник ). Никакого «нативного приведения» не происходит, вы можете преобразовать что-нибудь подобное, просто результат может быть не очень полезным в некоторых случаях! (но нулевое поведение вполне может быть).
VisualMelon
1
Альтернатива - интерполяция строк :$"{n}"
Андрей Толстой
41

Однажды я специально поместил свою программу, namespace Systemчтобы сократить доступ к определенному классу. сравнить

using System;using M=System.Math;

в

namespace System{using M=Math;
детеныш
источник
9
Просто помните, что лучше полностью квалифицировать классы / функции, когда единственное использование решает проблему. Это полезно только в том случае, если вам нужно вызывать что-то более одного раза, и даже тогда только для элементов в Systemпространстве имен.
Ник Ларсен
Вы также можете просто сделать using System;class P....
Идам
@Logan: речь шла не только using System;о псевдониме для класса в том же пространстве имен, что короче, чем я здесь показал.
Джои
В using static System.Math;C # 6 это еще короче (добавьте, что вы можете использовать любую из этих функций, как если бы они были действительно глобальными, а не в классе). Исходное предложение может быть еще короче, чем using staticесли вам нужно получить доступ к нескольким классам.
молоко
@milk: дополнительное staticключевое слово часто дольше, чем любая экономия от пропуска M.вызовов методов, но да, это вариант, но он требует огромных первоначальных затрат, которые требуют много вызовов для амортизации.
Джои
30

Используйте varдля объявления и инициализации (одиночных) переменных, чтобы сохранить символы типа:

string x="abc";

становится

var x="abc";

intКонечно, это не обязательно .

детеныш
источник
2
Помните, что varне может иметь несколько объявлений, например, var x="x",y="y";это невозможно.
Ян Х.
29

При использовании LINQ вы можете передать метод напрямую Selectвместо создания лямбды.

Итак, вместо

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

вы можете использовать

foo.Select(int.Parse)

непосредственно.

(Обнаружен недавно при улучшении одного из ответов Тимви на C # .)

детеныш
источник
2
FWITW это называется η-сокращением
ThreeFx
Он также известен как стиль «без очков»
Джонатан Уилсон,
5
Для более прагматичных из нас это просто короче : -þ
Джои
23

Помните, что самая маленькая компилируемая программа на C # состоит из 29 символов:

class P
{
    static void Main()
    {   
    }
}

Итак, начните с удаления этого из вашей длины и судите свой ответ о том, сколько это займет. C # не может конкурировать с другими языками, когда речь идет о печати или чтении ввода, что является сердцем большинства[code-golf] проблем, поэтому не беспокойтесь об этом. Как игрок в C #, вы действительно соревнуетесь с языком.

Несколько других вещей, которые нужно иметь в виду:

  • Уменьшите все петли и if операторы до одной строки, если это возможно, чтобы снять скобки.
  • Если задана опция между stdin и командной строкой, всегда используйте командную строку!
Ник Ларсен
источник
Обычно это также
касается
1
As a C# golfer, you're really competing against the language Невероятно связанный
дорукайхан
1
На самом деле это не так. Он также компилируется с использованием static int Main()28 символов.
Метония
21

Вместо

bool a = true;
bool b = false;

делать

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

Если вам нужно несколько переменных, используйте это (предложено @VisualMelon )

bool a=0<1,b=!a;
Yytsi
источник
Обратите внимание, что если вам нужно несколько переменных одного типа, обычно дешевле объявить тип запятой, bool a=0<1,b=!a;
разделив
18

Пользуйся троичным оператором if ..else блоками, где это уместно.

Например:

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

эффективнее:

j=i<1?1:0;
Nellius
источник
15
Я единственный, кто чувствует, что второй случай по своей природе более читабелен для подобных вещей в целом? Я делаю это регулярно. Кроме того, если мне нужно избежать нулевого условия (например, для строки), я делаю что-то вроде var x = input ?? "";(я люблю свои объединения)
jcolebrand
Есть моменты, когда это далеко не самый читаемый вариант, особенно когда i < 1это сложный оператор или когда имя jдлинное. ИМО, он также не очень хорошо передает побочные эффекты. В случае, когда if (i < 1)что-то вроде if (SendEmail(recipient))этого возвращает true / false в зависимости от успешности побочных эффектов, я предпочитаю нотацию if / then.
Ник Ларсен
11
Нет необходимости в скобках во втором случае - j=i<1?1:0;достаточно.
Данко Дурбич
3
Вопрос требует советов, которые являются специфическими для C #. Это входит в советы для всех языков .
Питер Тейлор
4
@PeterTaylor Я ответил на этот вопрос более 3 лет назад, задолго до того, как была создана тема, на которую вы
ссылались
15

Эффективное использование использования

Вы можете заменить float (это псевдоним System.Single) с zпомощьюz=System.Single;

Затем замените z=System.Single;с z=Single;помощью размещения программы в пространстве именSystem . (Как и с ответом Джои)

Это может быть применено для других типов значений (используйте то, для чего они являются псевдонимами), структур и классов.

Натан Купер
источник
14

Если вам нужно использовать Console.ReadLine()несколько раз в вашем коде (минимум 3 раза), вы можете сделать:

Func<string>r=Console.ReadLine;

а потом просто использовать

r()

вместо

Кристиан Лупаску
источник
Я думаю, вам нужно убрать ()с первой строки.
mellamokb
@ mellamokb это верно, спасибо! исправлено.
Кристиан Лупаску
1
Вы не можете сделать auto r=Console.ReadLine;?
Клавдиу
2
@claudiu нет, к сожалению, не ideone.com/jFsVPX
Кристиан Лупаску,
@ Клаудиу, autoэто C++глагол. varдля C#. Причина, по которой это невозможно сделать, заключается в том, что Console.ReadLineона перегружена, поэтому необходимо указать сигнатуру функции, чтобы сообщить компилятору, какая перегрузка нужна.
GreatAndPowerfulOz
14

При чтении каждого символа аргумента командной строки, а не зацикливания до длины строки:

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

Вы можете сохранить символ, используя блок try / catch, чтобы найти конец:

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

Это относится к любому массиву в массиве, например:

  • string[]
  • int[][]
  • IList<IList<T>>
Hand-E-Food
источник
7
Это действительно ужасно ... Я люблю это!
Алекс Рейкинг
черт
Это действительно зло!
GreatAndPowerfulOz
13

Используйте лямбда-выражения для определения функции в C # 6

В C # 6 вы можете использовать лямбду для определения функции:

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

Это короче, чем определение такой функции:

int s(int a,int b){return a+b;}
ProgramFOX
источник
3
C # 6 дает целый ряд новых возможностей для код-гольфа
Jcolebrand
В C # 7 это можно сделать внутри другой функции для создания локальных функций. Я сомневаюсь, что это поможет во время игры в гольф, но это все еще просто ловкий трюк, чтобы знать.
TehPers
1
Это не формально лямбда. Это член с выражением лица .
рекурсивный
13

LINQ

Вместо того, чтобы использовать:

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

чтобы получить Перечислимый с результатом функции fдля каждого intв [0,y]вы можете использовать

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

если вам нужно stringили что-то, что реализуется Enumerableв вашей программе, вы можете использовать их тоже

var s="I need this anyway";
s.Select((_,i)=>f(i))
зазубренный
источник
Я использую этот трюк в своем ответе на вызов Шамира «Секретный обмен» .
Aloisdg говорит восстановить Монику
Я не думаю, что строковая часть будет выполняться, если вы не выполните итерирование с включенной оптимизацией. Просто не удалось для меня, пока я не сделал .ToArray () ;. Помимо этого, удивительный совет!
Gaspa79
Да, перечисления ленивы, но это верно для всех трех примеров, а не только для одного со строкой.
грязное
11

Если вам нужно использовать универсальный Dictionary<TKey, TValue>по крайней мере два раза в вашем коде, вы можете объявить класс словаря, как в этом примере:

class D:Dictionary<int,string>{}

а потом просто использовать

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

вместо повторения Dictionary<int,string> для каждого экземпляра.

Я использовал эту технику в этом ответе

Кристиан Лупаску
источник
2
А также «D d» вместо «VAR D»
Zukki
@Zukki Очевидно! О чем я только думал? :)
Кристиан Лупаску
1
Альтернатива:using D = System.Collections.Generic.Dictionary<int,string>;
Андрей Толстой
10

Вы можете использовать floatи doubleлитералы, чтобы сохранить несколько байтов.

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

Когда вам нужна некоторая intарифметика для возврата floatили doubleвы можете использовать литералы для принудительного преобразования.

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

Если вы когда-нибудь столкнетесь с ситуацией, когда вам нужно выполнить приведение, вы можете сохранить несколько байтов, используя вместо этого умножение.

((double)x-y)/(x*y);
(x*1d-y)/(x*y);      // saves 5 bytes
SLuck49
источник
Еще короче:(x-y)*1d/x/y;
рекурсивный
9

Помните, где частное или публичное присуще, например, следующее:

class Default{static void Main()

по сравнению с

public class Default { public static void Main()
jcolebrand
источник
5
И всегда делайте в классе только одну букву :-)
Джои
2
Да, и еще одна приятная вещь, подразумеваемая здесь: Mainне нужно никаких аргументов в отличие от Java, например.
Джои
@Joey: и при этом это не должно быть общедоступным.
Р. Мартиньо Фернандес
1
@martinho ~ ты прочитал мой ответ? ;) нет публики на главной
Jcolebrand
@Joey ~ Я пытался сохранить один из них на пост;) ... думал, что кто-то еще будет писать о том, что основной или классы - это только одно письмо. Видя, как никто другой, я тоже добавлю это.
Jcolebrand
9

Для однострочных лямбда-выражений можно пропустить скобки и точку с запятой. Для однопараметрических выражений вы можете пропустить круглые скобки.

Вместо

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

использование

SomeCall(x=>DoSomething);
Джулиана Пенья
источник
11
Я никогда не пишу скобки для однопараметрических лямбд, даже в производственном коде.
Р. Мартиньо Фернандес
Я всегда использую скобки, потому что мне нравится разбивать лямбду на несколько строк для удобства чтения.
Джулиана Пенья
3
SomeCall(DoSomething)еще лучше
GreatAndPowerfulOz
9

Циклический:

Переменные объявления:

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

стали:

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

И если у вас есть необходимость или работа с переменной i только один раз, вы можете начать с -1 (или 0 в зависимости от обстоятельств цикла) и увеличить значение inline:

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

в

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

И это уменьшает на один символ, а также немного запутывает код. Сделайте это только для ПЕРВОЙ iссылки, например, так: (если оптимизация одного символа невелика, но она может помочь)

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

в

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

когда цикл не должен увеличиваться i(обратный цикл заказа):

for(int i=MAX;--i>0;){
      Console.WriteLine(i);
}
jcolebrand
источник
++В таких случаях я обычно помещаю непосредственно в заголовок цикла: за for(;++i<max;)ним легче следовать, а с труднее ошибиться.
Джои
@Joey В этих случаях я склонен переключаться на while (++ i <max), которое имеет ту же длину, но легче для чтения.
ICR
ICR: зависит от того, можете ли вы добавить еще один (более ранний) оператор в forзаголовок, который затем снова сохранит символ.
Джои
Вы можете переместить обе декларации обратно в предложение for для экономии байтов a1.
рекурсивный
Мне нравится меняться for(;i<max;)на while(i<max). Такое же количество байтов, но для меня это выглядит чище.
Ayb4btu
8

Существуют обстоятельства, когда выходной параметр может сохранять символы. Вот немного надуманный пример - алгоритм 10-пинового боулинга.

С возвратом заявления:

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

И с выходным параметром:

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

Выходной параметр здесь сохраняет в общей сложности 5 символов.

M_J_O_N_E_S
источник
8

В C # нам не разрешено if(n%2)проверять, nявляется ли число четным. Если мы это сделаем, мы получим cannot implicity convert int to bool. Наивная обработка будет делать:

if(n%2==0)

Лучше всего использовать:

if(n%2<1)

Я использовал это, чтобы получить один байт здесь .

обратите внимание, что это работает только для положительных чисел, так как -1%2==-1это считается даже с этим методом.

Aloisdg говорит восстановить Монику
источник
6

Строковая интерполяция

По-настоящему простым экономящим место улучшением является интерполяция. Вместо:

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

просто используйте $встроенные выражения:

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

Это, вместе с новыми телами выражений в C # 6.0, должно сделать любую простую задачу вычисления строк довольно удобной в C #.

mınxomaτ
источник
3
Также обратите внимание, что i+$" bottles of beer";короче $"{i} bottles of beer".
говорит aloisdg Восстановить Монику
1
@aloisdg В этом первом случае вы должны уйти $, хотя.
Метония
@Metoniem Действительно! Я позволил это, потому что в моем первоначальном случае у меня было два, {i}один спереди и один посередине;)
aloisdg говорит: «Восстановите Монику
@aloisdg Ааа, понятно. Да, комментарии позора не могут быть отредактированы :(
Metoniem
6

Используйте C # лямбду. Поскольку PPCG позволяет использовать лямбда для ввода / вывода, мы должны их использовать.

Классические методы C # выглядят так:

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

Как лямбда напишем

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

Анонимная лямбда тоже допускается:

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

Убери весь шум и сфокусируйся!

Обновить:

Мы можем улучшить один шаг больше с выделкой , как @TheLethalCoder комментарий:

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

Пример curring от @Felix Palmen: Как вычислить ключ WPA?

Будет полезно, когда у вас будет ровно 2 параметра, тогда _лучше использовать пустую неиспользуемую переменную . Смотрите мета-пост об этом . Я использую этот трюк здесь . Вам придется немного изменить функцию. Пример: попробуйте онлайн!

Aloisdg говорит восстановить Монику
источник
1
Не уверен, что это где-то еще в советах, но для этого примера вы тоже можете использовать карри ...s=>c=>...
TheLethalCoder
@TheLethalCoder Действительно, мы можем! Я обновлю ответ, спасибо!
Aloisdg говорит восстановить Монику
Можете ли вы использовать Eta-Reduction в этом случае? Что - то вроде этого: s=>s.Contains.
corvus_192
Заметьте, что ответы на C # и Java о разновидности «нетипизированная лямбда» теряют популярность, возможно, вы захотите присоединиться к обсуждению этого мета-поста . Предлагаемая альтернатива(string s,char c)=>s.Contains(c)
VisualMelon
Обсуждение неиспользованных функций
говорит aloisdg Восстановить Монику
5

Сделайте имена классов только одной буквой. Улучшение на Советы для кода игры в гольф в C # мы идем от

class Default{static void Main()

в

class D{static void Main()

который выбивает еще 6 символов в этом случае.

jcolebrand
источник
1
То же самое
11
Как это "по крайней мере несколько специфично для C #"?
Питер Тейлор
5

Метод Computeinstance System.Data.DataTable, позволяет оценить простое строковое выражение, например:

C # (компилятор Visual C #) , 166 байт

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

Попробуйте онлайн!

Не очень "гольф" как таковой, но иногда может быть полезным.

digEmAll
источник
5

Обмен двух переменных

Обычно, чтобы поменять местами две переменные, вы должны объявить временную переменную для хранения значения. Это будет выглядеть примерно так:

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

Это 16 байтов! Есть некоторые другие методы обмена, которые лучше.

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

Последние три работают только для числовых значений, и, как указано только в ASCII, последние два могут привести к исключению ArithmeticOverflow. Все вышеперечисленное имеет размер 12 байт, что позволяет сэкономить 4 байта по сравнению с первым примером.

Воплощение невежества
источник
Тем не менее, применяется только к числам, и даже тогда все, кроме кортежей и xor, может привести к целочисленным ограничениям, если вы применяете это к целым числам. Иногда и другие типы чисел также попадают в пределы
только ASCII
4

Использование LinqPad даст вам возможность удалить все накладные расходы программы, поскольку вы можете выполнять операторы напрямую. (И это должно быть полностью законно в Codegolf ... Никто не говорит, что вам нужен .exe)

Вывод осуществляется с использованием .Dump()метода расширения.

EvilFonti
источник
.NetFiddle support .Dump();)
aloisdg сообщает восстановить Monica
4

(Частный случай знания вашего приоритета оператора !)

Используйте %для плотного (несколько) ограниченного вычитания. Это может сэкономить вам пару скобок вокруг вычитания, результат которого вы хотите умножить или разделить на что-то; но будьте осторожны, у него есть серьезные ограничения.

Вместо

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)

Рассмотреть возможность

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

Примеры:

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

Я только что обнаружил это, и я чувствую, что это будет полезно помнить при работе с ASCII в будущем. (В настоящее время я играю в гольф где-то, где я использую ASCII для компактных числовых представлений, но нужно умножить на 1или -1на основе другого условия, и это полосатый 2 байта)

VisualMelon
источник
4

Если вам нужно включить несколько usings, которые выпадают из одной и той же иерархии, часто короче использовать самый длинный в качестве namespace:

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

против:

namespace System.Linq
{
    //Some code
}
TheLethalCoder
источник
3

Обнаружен сегодня вечером "в окопах", улучшая при этом некоторый гольф-код ... если у вас есть класс для обработки, вы можете выполнить работу в конструкторе, чтобы сохранить объявление метода.

Я обнаружил это при сокращении консольного приложения - как было static void Main() , все функции и переменные должны были быть объявлены как статические. Я создал вложенный класс с функциями-членами и переменными, основная работа была выполнена в конструкторе. Это также сохраняет символы в коде вызова.

например, класс с методом:

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

Класс с работой в конструкторе:

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

Этот пример сохраняет 9 символов.

M_J_O_N_E_S
источник
3

Используйте Actionкак, Funcчтобы установить функцию для переменной. Actionничего не возвращает ( void), так что это отлично подходит для печати.

Например:

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

Эти советы вдохновлены @ W0lf отличным примером использования FuncсReadLine .

aloisdg говорит восстановить Монику
источник
3

Объявлять пустые / совпадающие строки вместе

Если вам нужно объявить несколько пустых / совпадающих строк, вы можете сохранить несколько байтов следующим образом:

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

К сожалению, var a="",b=a,c=a;это незаконно, так какimplicitly type variable cannot have multiple declarators

Эрресен
источник
Можете ли вы сделать var a=b=c=""как в JavaScript?
corvus_192
@ corvus_192 Нет, к сожалению, нет.
Erresen