Поради щодо гольф-коду в C #


62

Які загальні поради щодо гольфу в C #? Я шукаю ідеї, які можна застосувати до проблем із гольфом в цілому, які принаймні дещо специфічні для C # (наприклад, "видалити коментарі" - це не відповідь). Будь ласка, опублікуйте одну пораду на відповідь.

- запозичені з ідеї Маркога;)


НАЙКРАЩІЙ ПОРАДУ => Використовуйте щось поряд із .NET, якщо ви не хочете подавати найдовшу відповідь на виклик. .NET розроблений, щоб бути дуже багатослівним і дозволити IDE робити друк. Що насправді не так вже й погано, як це звучить для загального програмування, якщо у вас є кокетка IDE, але для гольфу з кодом ця стратегія, безумовно, не вдається.
krowe

Пробачте за картинку календаря, це було все, що я міг знайти за короткий час.
підземниймонорельс

Відповіді:


59

Замість .ToString()використання +""для використання числових чисел та інших типів, які можна спокійно передати на рядок.

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

5
Це також працює в JS.
Кіос

1
Зазвичай це 5 символів, якщо вам потрібно потім використовувати рядок для включення дужок ...(1+"").DoSomethingWith1String();
TheLethalCoder

1
Якщо вам потрібна рядок, ви її зазвичай зберігаєте. Майже про будь-яке інше використання може спочатку зробити висновок ToString () ...
jcolebrand

1
Зауважте, що це насправді викликає статичний 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....
ldam

@Logan: Мова йшла не тільки using System;про те, а й про те, щоб мати псевдонім для класу в тому ж просторі імен, що коротше, як я показав тут.
Joey

Це ще коротше, ніж це робити using static System.Math;в C # 6 (додайте, що ви можете використовувати будь-яку з цих функцій так, ніби вони справді глобальні - не в класі). Оригінальна пропозиція все ще може бути коротшою, ніж using staticякщо вам потрібно отримати доступ до декількох класів.
молоко

@milk: додаткове staticключове слово часто довше, ніж будь-яка економія від виходу з M.методу викликів, але так, це варіант, але він коштує з величезною попередньою вартістю, яка потребує великої кількості дзвінків для амортизації.
Joey

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
Для більш прагматичних із нас це просто коротше : -þ
Joey

23

Пам'ятайте, що найменша програма для компіляції на C # - 29 символів:

class P
{
    static void Main()
    {   
    }
}

Тож почніть з видалення цього з своєї довжини і оцініть свою відповідь на те, скільки потрібно. C # не може конкурувати з іншими мовами, що стосується друку чи читання тексту, що є серцем більшості [code-golf]проблем, тому не хвилюйтеся з цього приводу. Як гольф C #, ви справді змагаєтесь проти мови.

Ще кілька речей, про які слід пам’ятати:

  • ifЯкщо можливо, зменшіть усі петлі та заяви до одного рядка, щоб видалити дужки.
  • Якщо вам надано можливість між stdin та командним рядком, завжди використовуйте командний рядок!

Зазвичай це стосується і потрійних;)
jcolebrand

1
As a C# golfer, you're really competing against the language Неймовірно споріднене
снідакайхан

1
Власне, це неправда. Він також компілює, використовуючи static int Main()28 символів.
Metoniem


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

18

Віддайте перевагу потрійному оператору над if.. elseблоками, де це доречно.

Наприклад:

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

більш ефективно:

j=i<1?1:0;

15
Чи я єдиний, хто відчуває, що другий випадок за своєю суттю є більш зрозумілим для таких речей? Я роблю це звичайно. Крім того, якщо мені потрібно уникнути нульового стану (наприклад, на рядку), я роблю щось на кшталт var x = input ?? "";(я люблю свої злиття)
jcolebrand

Бувають випадки, коли це далеко не самий читабельний варіант, особливо коли i < 1це складний вислів або коли ім'я jдовге. IMO, він також не вдається дуже добре передати побічні ефекти. У випадку, коли if (i < 1)щось таке, if (SendEmail(recipient))що повертає правду / хибність залежно від успіху побічних ефектів, я віддаю перевагу позначенню if / then.
Нік Ларсен

11
Немає потреби в дужках у другому випадку - j=i<1?1:0;достатньо.
Данько Дурбич

3
Питання задає поради, які є дещо характерними для C #. Це один із порад для всіх мов .
Пітер Тейлор

4
@PeterTaylor Я відповів на це запитання понад 3 роки тому, задовго до того, як була створена нитка, яку ви пов’язали
Nellius

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
Крістіан Лупаску

@Claudiu, 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 дає абсолютно новий спектр можливостей
кодувати

У 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 Очевидно! Що я думав? :)
Крістіан Лупаску

Альтернатива:using D = System.Collections.Generic.Dictionary<int,string>;
Андрій Толстой

10

Ви можете використовувати floatі doubleлітерали, щоб зберегти кілька байт.

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

Коли вам потрібна деяка intарифметика для повернення a 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

@Joey: і це не повинно бути публічним.
Р. Мартіньо Фернандес

1
@martinho ~ ти читав мою відповідь? ;) немає публіки на main
jcolebrand

@Joey ~ Я намагався дотримати його до однієї за посаду;) ... подумав, що хтось інший буде публікувати що-небудь про основні чи класи лише одним листом. Бачачи, як ніхто інший, я продовжую додавати і його.
jcolebrand

9

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

Замість

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

Використовуйте

SomeCall(x=>DoSomething);

11
Я ніколи не пишу дужки для однопараметричних лямбда, навіть у виробничому коді.
Р. Мартіньо Фернандес

Я завжди використовую дужки, тому що люблю розділяти лямбду на кілька ліній для читабельності.
Джуліана Пенья

2
SomeCall(DoSomething)ще краще
GreatAndPowerfulOz

9

Цикл:

Змінні декларації:

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

стати:

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

І якщо у вас є потреба або працювати зі змінною i лише один раз, ви можете почати з -1 (або 0, залежно від обставини циклу) та збільшення кроку:

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

@Joey У цих випадках я схильний переходити на while (++ i <max), який має однакову довжину, але легше читати.
ICR

ICR: залежить від того, чи можете ви також помістити в forзаголовок ще одне (попереднє) твердження , яке потім збереже символ.
Джої

Ви можете перемістити обидві декларації назад у пункт для економії a1 байтів.
рекурсивна

Мені подобається перехід for(;i<max;)на while(i<max). Така сама кількість байтів, але для мене це просто виглядає чистіше.
Айб4бту

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

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

Приклад викривлення за допомогою @Felix Palmen: Як обчислити ключ WPA?

Буде корисно, коли у вас рівно два параметри, тоді пуста невикористана змінна _буде кращою. Дивіться мета-повідомлення про це . Я використовую цей трюк тут . Вам доведеться трохи змінити функцію. Приклад: Спробуйте в Інтернеті!


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

@TheLethalCoder Дійсно ми можемо! Я оновлю відповідь спасибі!
aloisdg

Чи можете ви використовувати ета-зменшення в цьому випадку? Що - щось на зразок цього: 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 символів у цьому випадку.


1
ditto зі змінними іменами
Nellius

11
Як це "принаймні дещо специфічно для C #"?
Пітер Тейлор

5

Метод Computeекземпляра 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 дасть вам можливість видалити всі накладні програми, оскільки ви можете виконувати операції безпосередньо. (І він повинен бути повністю законним у кодегольфі ... Ніхто не каже, що вам потрібен .exe)

Виведення відбувається за допомогою .Dump()методу розширення.


.NetFiddle підтримка .Dump();)
aloisdg

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

Якщо вам потрібно включити кілька using s, що всі випадають з однієї ієрархії, найчастіше коротше використовувати найдовший як namespace:

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

vs:

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

Декларуйте порожні / відповідні рядки разом

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

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 nope - на жаль, ні.
Ерресен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.