C ++ 'da golf oynamak için ipuçları


48

C ++ 'da golf oynamak için hangi genel ipuçlarınız var? Genel olarak golf problemlerini kodlamak için uygulanabilecek fikirleri arıyorum, en azından C ++ 'a özgüdür (örneğin, "yorumları kaldır" bir cevap değildir). Lütfen cevap başına bir ipucu gönderin.


4
C'de golf oynamak için ipuçlarının birçoğu C ++ için de geçerlidir, bu nedenle lütfen okuyucuların bu soruyu bildiğini varsayalım; Sadece geçerli bir C golf bahşişi olmayan bir şeye sahipseniz buraya gönderin.
Toby Speight

@TobySpeight Muhtemelen soru kimliği dışında aynı URL’ye sahip oldukları için.
NoOneIsHere

C ve C ++, 'golf' türü olmasa bile, doğru ve kolaydır (biri C ++ 'in alt grubunu
düşünürse

Yanıtlar:


24

Üçlü koşullu operatör ?:genellikle basit bir dayanak noktası olarak kullanılabilir if- elsehatırı sayılır tasarruflarla açıklamalar.

Olduğu gibi alternatif değerleri seçmek için kullanılabileceği için özel bir değere sahiptir.

#include <iostream>
#include <cstdlib>
int main(int c, char**v){
  int o=0,e=0,u;
  while(--c) ((u=atoi(v[c]))%2?o:e)+=u;
  std::cout << "Sum of odds " << o <<std::endl
            << "Sum of evens " << e <<std::endl;
}

Henüz kodu çalıştırmadım, ama söylediğiniz gibi çalıştığını sanmıyorum. ((u = atoi (v [c]))% 2? o: e) + = u, sol veya soldaki ifadeye u değerini eklemekten başka bir şey yapmaz, o veya e değerini alır, ancak o ve e değişkenleri değişmeden kalırlar, böylece her zaman 0 olacaktır. çalışması için adresleri kullanmalısınız
Bogdan Alexandru

4
@BogdanAlexandru Er ... çalıştır. Gerçekten işe yarıyor. Parantez ifadesinin değeri bir bir referans ya da diğeri eve o. Bunun, operatörün bu hilenin işe yaramadığı yerlerde nasıl çalıştığından farklı olduğunu unutmayın çünkü bir değer olamaz.
dmckee

Bununla değiştirin std::endl, '\n'5 karakter kazandırır
Mukul Kumar

3
@MukulKumar Evet, evet. Ancak bu ipucunu gösterme amaçları için üçlü-koşulsuz golf oynamak için her şeyi açıklığa kavuşturdum.
dmckee

22

Bazen statik depolama süresi değişkenlerinin (özellikle tüm genel kapsam değişkenlerini içeren) başlangıçta otomatik olarak sıfır başlatılması (böyle bir garantinizin olmadığı otomatik değişkenlerin aksine) gerçeğini kullanarak iki karakter kaydedebilirsiniz. Yani yerine

int main()
{
  int a=0;
  // ...
}

Yazabilirsin

int a;
int main()
{
  // ...
}

+1 ama kesinlikle kötü bir uygulama
mondlos

@mondlos: Golf oynamak temel olarak kötü bir uygulama anlamına gelir.
celtschk

15

Bazı derleyiciler (örneğin, GCC) çok karakterli sabitleri destekler . Bu büyük bir tamsayı değeri gerektiğinde birkaç karakter kaydedebilir. Örnek:

int n='  ';

Değer uygulamaya özeldir. Genellikle değeri 'ab'olduğu 256*'a'+'b'veya 'a'+256*'b'. Tırnak işaretleri arasında en fazla 4 karakter belirleyebilirsiniz.


3
GCC? G ++ 'ı mı kastediyorsun ?
Nathan Osman,

6
@George Edison: GCC , C, C ++, Go, vb. Olanlar da dahil olmak üzere tüm ön uçlarını kapsayan GNU Derleyici Koleksiyonunun kısaltmasıdır .
Joey Adams

@Joey: Biliyorum, ama aynı zamanda GNU C Derleyicisinin adı.
Nathan Osman

25
@ George: GNU C derleyicisine GCC değil, gcc adı verilir.
fredoverflow

Bunu hatırlayabilsem de, unutabilirim.

12

Kullanışlı bulduğum bir tane:

Sıfır olmayan değerlerin trueboolean ifadelerinde x&&ydeğerlendirilmesinden x*yve booleanlarla uğraşırken değerlendirilmesinden faydalanmak

(x!=0 && y!=0)

için değerlendirir

(x*y)

Sadece aşağıda belirtildiği gibi taşmaların farkında olmalısınız.


2
Teknik olarak, bu x!=0 && y!=0. Ancak çarpımı kullanırken taşmalara dikkat etmeniz gerekir. 32-bit tamsayılar kullanıldığında, x = y = 65536 (ve diğer iki güç birleşimi birleşimi), x * y = 0 verir .
Martin Ender

Evet bu doğru. On iki boyutlu dizi sınırları olarak kontrol burada kullandım: codegolf.stackexchange.com/a/37571/31477 nerede önemli değildi. Bu noktaları düzenleyeceğim.
Baldrickk

1
Ancak, &&bunun *eksik olan bir kısa devre davranışına sahip olduğuna dikkat edin . Örneğin, i++!=0&&j++!=0ile değiştiremezsiniz i++*j++.
celtschk

@celtschk evet, iyi nokta. Ama tamamen boolean cebirini yapıyorsanız, o zaman işe yarıyor
Baldrickk

11

Aşağıdaki türleri kullanın:

u64, s64, u32, s32 (or int)

Tekrarlayan kelimeler / türler için #definesşunları kullanın :

#define a while

whileFazladan 10 karakter için telafi etmek için çok kullanıyorsanız buna değer . ( Yaklaşık 4 )


1
U64, s64, u32 ve s32 tipleri, C ++ 'ın bir parçası değildir. Derleyicinizin standart olmayan bir uzantısı olabilirler (ama onları hiç görmedim).
celtschk

5
Bu iki ipucu, ayrı ayrı oylanabilecekleri iki ayrı cevaba daha iyi yerleştirilebilir.
trichoplax


10

Mümkün olduğunda, değiştirmek &&ve ||için &ve |sırasıyla.

Basit if ifadeleri kullanılırken:

if(<condition>)<stuff>;

değiştirilebilir:

<condition>?<stuff>:<any single letter variable>;

hangi bir karakter kaydeder.


8

Kullanmak yerine while(1)kullanmak for(;;), bir karakter kaydetmek :)


8

Virgül operatörünü açık ve kapalı ayraçlar yerine kullanmak, maddelerinizde birden fazla ifadenin bulunduğu bir durum varsa, birkaç karakter kaydedebilir:

if(c){x=1;cout<<"Hi";y=2;}else{x=2;cout<<"Bye";y=3;}

vs.

if(c)x=1,cout<<"Hi",y=2;else x=2,cout<<"Bye",y=3;###

Düz bir IF'ye kaydedilen iki karakter veya bir IF / ELSE için toplam üç karakter.

C ve C ++ arasındaki bir ayrım noktası olarak, bir bütün olarak C ++ 'daki virgül ifadesinin sonucu bir değer olarak kullanılabilir ... FWIW.


7

Dizi öğeleri, böyle bir şey yerine doğrudan bellekte birbiri ardına depolandığından:

for(int x = 0; x < 25; x++) {
    for(int y = 0; y < 25; y++)
        array[x][y] = whatever;
}

Böyle bir şey yapabilirsiniz:

int* pointer = array;
for(int i = 0; i < 25*25; i++, pointer++)
    *pointer = whatever;

Açıkçası, yukarıdakilerin hiçbiri okunabilirlik için golf oynamıyor, ancak açıkça işaretçileri kullanmak size çok fazla alan kazandırabilir.


Unutma, bütün bu boşlukları kesebilirsin! (Tamamen farklı ipucu, ancak belirtilmelidir)
stokastic

@stokastic Örnekler sadece tekniğin nasıl kullanılacağını göstermek için golf edilmek değildir.
Dublör

6
neden olmasın for(int* i=array; i<array+25*25; i++)? O zaman sadece bir değişkeni takip etmeniz gerekir.
Lucas

6

Oldukça belirgin bir tane, ancak standart kütüphanenin çoğunu kullanıyorsanız, using namespace std;birkaç karakter kaydedebilirsiniz.


5
Yalnızca tek bir ad kullanıyorsanız, ancak bu oldukça sık using std::name;olsa da, daha kısa olabilir.
celtschk

10
Bu, yalnızca std::beş veya daha fazla kez kullanıyorsanız karakterleri kaydeder .
nyuszika7h

6

Hatırlamakta fayda var a[i]aynıdır *(a+i).

Değiştir a[0]ile *aiki karakter tasarrufu için. Ayrıca, a[i][0]eşdeğerdir *a[i]ve a[0][i]küçülür i[*a]. Eğer 0dizinizdeki bir dizini zor kodluyorsanız, muhtemelen daha iyi bir yol vardır.


5

10 büyük güçler yazmak yerine, gösterimi kullanın . Örneğin a=1000000000, daha uzun a=1e9. Bu a=1e9+24daha iyi gibi diğer numaralara uzatılabilir a=1000000024.


1
Bunun tam olarak eşdeğer olmadığını, kullanmadan önce tamsayı türlerine basmanız gerektiğini unutmayın. Örneğin veya ile 1e9/xaynı değildir . 1000000000/xint(1e9)/x
user202729

5

Üçlü işlecini ?:true bloğunda hiçbir ifade olmadan kullanabilirsiniz (bayt kazandırır)

#include <iostream>

int foo()
{
    std::cout << "Foo\n";
}

int main()
{
    1?foo():0;  // if (true) foo()
    0?:foo();   // if (!false) foo()
}

Buradan kontrol edin


5
Bu bir GNU uzantısı gibi görünüyor, C ++ standardında değil. https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Conditionals.html#Conditionals
ceilingcat

r? fan () 0; // eğer (r) foo () bu tamam ;;;;; ama Bunun için r?: foo (); Bilmiyorum
RosLuP

5

Daha kısa başlık

Bu GCC'ye özgüdür, diğer derleyicilere genişletilebilir.

Önceden derlenmiş başlık.

G ++ bits/stdc++.h'da önceden derlenmiş başlık diğer tüm başlıklardan oluşur. import2 farklı olana ihtiyacınız varsa, sadece bunu kullanabilirsiniz.

Daha kısa başlık.

Bu, http://en.cppreference.com/w/cpp/header adresinde listelenen başlıkların tamamıdır :

uzunluk sırasına göre sıralanır.

Bazıları zaten daha uzun bits/stdc++.h, bazıları ise C ++ 17 desteği gerektiriyor. Bazıları TIO G ++ tarafından desteklenmiyor (bilmediğim sebeplerden dolayı). Bunları filtrelendik:

Bazılarının daha kısa olanlarla yer değiştirmesi olabilir. İhtiyacınız olanın değiştirilebilir olup olmadığını sadece ikili arama yapın. Özellikle:

cstdio -> ios        (-3 bytes)
algorithm -> regex   (-4 bytes)
vector -> queue      (-1 byte)
string -> map        (-3 bytes)
bitset -> regex      (-1 byte)
numeric -> random    (-1 byte)

4

#importyerine #includebir bayt daha verir.

Ayrıca, #importbaşlık ile başlık arasındaki boşluk karakteri mutlaka değildir:

#include <map>
// vs
#import<map>

stdlibÜstbilgiden bir şeye ihtiyacınız olursa , herhangi bir üstbilgiyi yerine STL kabı (tercihen setveya map) ile alabilirsiniz cstdlib.


3

Booleanlarda aritmetik işlemler:

olmasına rağmen

a*=b>0?.5:-.5

daha iyi

if(b>0)a*=.5;else a*=-.5;

o kadar iyi değil

a*=(b>0)-.5

Ayrıca, çok kullanılan bir şey üzerinde #define komutunu kullanmak. Tip isimleri gerekli olmadığından, genellikle işlevleri kullanmaktan daha kısadır.

Şeyleri mümkün olduğunca birleştirin:

a+=a--;

aynıdır

a=2*a-1;

Örnekleriniz doğru olsa da, xbir değer x++olarak ve bir değer olarak kullanırken tanımsız davranışları çağırmaya dikkat edin. tanımsız davranış ve sıralama noktaları
ceilingcat

Evet mümkün a + = a--; Tanımsız Davranışı
Var

3

Genel lambdas'ı ucuz şablon olarak kullanın

Bunun dışındaki türler için int, onları işlev argümanları olarak kullanmak pahalı olabilir. Bununla birlikte, jenerik lambdalar tanıtıldı (C ++ 14? De) ve herhangi bir lambranın bir şablon olmasına izin verin - autoargüman türlerini kullanarak baytları kurtarabilir. Karşılaştırmak:

double f(double x, double y)
[](auto x, auto y)

Genel lambdalar, yineleyicileri kabul etmek için de çok uygundur - muhtemelen C ++ 'da dizi girişlerini kabul etmenin en iyi yolu [](auto a, auto z), / ve / / dizinin / etc dizininin olduğu ave zgeçtiği yerdir .begin()end()


2

Golf kodundaki ilk denememde "Sonraki numaraları çıkar" görevinden başladım (58 byte)

int f(int N, int P){int F;for(F=N;P;F-=++N,P--);return F;}

daha sonra güvenli 5 bayt lambda kayması ve başlangıç ​​ayarlarının for(53) dışına çıkarılmasıyla güvenli

[](int N,int P){int F=N;for(;P;F-=++N,P--);return F;}

ve son olarak geçtikten sonra forhiç whileI 51 bayt var:

[](int N,int P){int F=N;while(P--)F-=++N;return F;}

Ungolfed test kodu gibi bir şey:

#include <iostream>
int main(void)
{
    int N, P;
    std::cin >> N >> P;
    auto f = [](int N,int P)
    {
        int F = N;
        while (P--)
            F -= ++N;
        return F;
    };
    std::cout << f(N, P) << std::endl;
    return 0;
}

GÜNCELLEME:

Aslında foraynı uzunlukta olabilir while:

[](int N,int P){int F=N;for(;P--;F-=++N);return F;}

2

Partiye geç kaldım sanırım ...

Bir ifadeyi, 0 ve 1 yerine -1 ve 1'e dönüştürmek istiyorsanız, bunun yerine:

int x;
if (a * 10 > 5)
    x = 1;
else
    x = -1;

Bunu yap:

int x = (a * 10 > 5) * 2 - 1;

Kullanıma bağlı olarak bazı baytları kaydedebilir.


Bunun yerine , 1 bayt daha kısa olan int x=(a*10>5)*2-1;, yapamadı int x=a*10>5?1:-1;mı?
girobuz

2

A ve b iki tamsayılı değişkeni değiştirmek isterseniz,

a^=b^=a^=b;

standart yoldan 5 karakter kaydederek kullanılabilir

a+=b;
b=a-b;
a-=b;

1
Bu standart yol hakkında. ,tdaha önce oluşturulan ve o zamandan t=a;a=b;b=t;beri zaten 3 byte daha kısa olurdu a+=b;b=a-b;a-=b;. Yine de, a^=b^=a^=b;bundan daha kısa, benden +1. C ++ bilmiyorum ama gerçekten işe yarıyor . Bir Java kod-golfçü olarak üzgünüm orada çalışmak görünmüyor üzgünüm . :(
Kevin Cruijssen

1
@KevinCruijssen Evet, C ++ 'dan bahsetmeliydim, çok fazla java tanımıyorum ama java'da a^=b;b^=a;a^=b;iyi çalışıyor.
joker007

1
Açıkça C ++ 'dan bahsetmeye gerek yok. Tüm bu ipuçları C ++ içindir. :) Bir Java geliştiricisi olarak Java'da benzer bir şey yapılıp yapılmadığını merak ettim ama görünüşe göre yapamadım. a^=b;b^=a;a^=b;gerçekten çalışır, ancak ,t+ dan daha uzundur t=a;a=b;b=t;. Java'dan bahsettiğim için üzgünüm, çünkü konu dışı. Ama C ++ codegolfers için güzel ipucu!
Kevin Cruijssen

2

İçe aktarmak yerine GCC yerleşiklerini kullanın

Bir GCC derleyicisi kullanıyorsanız, bazen __builtin_putsveya gibi yerleşik işlevlerini kullanmanıza yardımcı olur __builtin_clz. Örneğin,

44 bayt:

int main(){__builtin_puts("Hello, world!");}`

50 bayt:

#import<cstdio>
int main(){puts("Hello, world!");}

1

Eğer C ++ 11 veya daha yenisini yapıyorsanız (ki şimdi her zaman olduğu gibi), automümkünse karmaşık türler için kullanın .

Örnek: 66 yerine 54 Bayt

#include<vector>
std::vector<int> f(std::vector<int> l){return l;}
#include<vector>
auto f(std::vector<int> l){return l;}

Ayrıca, performans önemli olmadığı için, bazı zorluklar std::listiçin bir iş sadece birkaç bayt için daha az iş yapabilir:

#include<list>
auto f(std::list<int> l){return l;}

1

Fonksiyonlar <algorithm>çoğunlukla geçmesini gerektirir a.begin(),a.end()yerine kullanabileceğiniz, gerçekten uzun &a[0],&*end(a)olursa 3 bayt kaydetmek için aise vectorya string.

sort(a.begin(),a.end());
sort(begin(a),end(a));
sort(&a[0],&*end(a));

0

Kullanmayın string(""), kullanmayın "". 8 bayt kaydeder.


Tam olarak eşdeğer değil. Örneğin "" + 'a'bir char* + charsüre, işaretçi ilaveli olan std::string("") + 'a'bir std::string + char- dize birleştirme. string()işe yarardı.
user202729
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.