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


62

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

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


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

Простите меня за фотографию календаря, это было все, что я смог найти за короткое время.
— подземный

Ответы:


59

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

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

5
Это также работает в JS.
— Cyoce

1
Обычно это 5 символов, если вам нужно использовать строку для включения фигурных скобок ...(1+"").DoSomethingWith1String();
— TheLethalCoder

1
Если вам нужна строка, вы обычно храните ее.
— Практически

1
Обратите внимание, что это фактически вызывает static String.Concat(object)с аргументом, а не виртуальный вызов object.ToString(). Concatявно преобразуется nullв пустую строку ( см. справочный источник ). Никакого «нативного приведения» не происходит, вы можете преобразовать что-нибудь подобное, просто результат может быть не очень полезным в некоторых случаях! (но нулевое поведение вполне может быть).
— VisualMelon


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;

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

18

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

Например:

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

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

j=i<1?1:0;

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>>

7
Это действительно ужасно ... Я люблю это!
— Алекс Рейкинг


Это действительно зло!
— GreatAndPowerfulOz

13

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

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

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

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

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

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


Я не думаю, что строковая часть будет выполняться, если вы не выполните итерирование с включенной оптимизацией. Просто не удалось для меня, пока я не сделал .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

Еще короче:(x-y)*1d/x/y;
— рекурсивный

9

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

class Default{static void Main()

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

public class Default { public static void Main()

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

++В таких случаях я обычно помещаю непосредственно в заголовок цикла: за 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 символов.


8

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

if(n%2==0)

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

if(n%2<1)

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

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


6

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

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

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

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

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

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


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


1
Не уверен, что это где-то еще в советах, но для этого примера вы тоже можете использовать карри ...s=>c=>...
— TheLethalCoder

@TheLethalCoder Действительно, мы можем! Я обновлю ответ, спасибо!
— Aloisdg говорит восстановить Монику

Можете ли вы использовать Eta-Reduction в этом случае? Что - то вроде этого: s=>s.Contains.
— corvus_192

Заметьте, что ответы на C # и Java о разновидности «нетипизированная лямбда» теряют популярность, возможно, вы захотите присоединиться к обсуждению этого мета-поста . Предлагаемая альтернатива(string s,char c)=>s.Contains(c)
— VisualMelon


5

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

class Default{static void Main()

в

class D{static void Main()

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



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",""));
        }
    }
}

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

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


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()метода расширения.



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 байта)


4

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

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

против:

namespace System.Linq
{
    //Some code
}

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 символов.


3

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

Например:

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

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


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.