23 personaggi unici usando Digraphs. (25 senza). No UB.
Utilizzare la sintassi dell'inizializzatore rinforzato C ++ 11 per elencare l'inizializzazione di un numero intero su zero int var{};evitando =e 0. (O nel tuo caso, evitando globale iiii). Questo ti dà una fonte di zeri diversa dalle variabili globali (che sono inizializzate staticamente a zero, a differenza dei locali).
I compilatori correnti accettano questa sintassi per impostazione predefinita, senza dover abilitare alcuna opzione speciale.
(Il trucco avvolgente intero è divertente, e va bene per giocare a golf con l'ottimizzazione disabilitata, ma l'overflow firmato è un comportamento indefinito in ISO C ++. L'ottimizzazione dell'ottimizzazione trasformerà quei loop avvolgenti in loop infiniti, a meno che non si compili con gcc / clang -fwrapvper fornire bene l'overflow di numeri interi firmati definito comportamento: avvolgente complemento di 2.
Curiosità: ISO C ++ std::atomic<int>ha un complemento di 2 ben definito! int32_tè necessario essere il complemento di 2 se definito affatto, ma il comportamento di overflow non è definito, quindi può ancora essere un typedef per into longsu qualsiasi macchina in cui uno di questi tipi è 32 bit, nessuna spaziatura e complemento di 2).
Non utile per questo caso specifico:
È inoltre possibile inizializzare una nuova variabile come copia di una esistente, con parentesi graffe o (con un inizializzatore non vuoto), parentesi per l'inizializzazione diretta .
int a(b)o int a{b}sono equivalenti aint a = b;
Ma int b();dichiara una funzione invece di una variabile inizializzata a zero.
Inoltre, è possibile ottenere uno zero con int()o char(), ovvero inizializzazione zero di un oggetto anonimo.
Possiamo sostituire i tuoi <=confronti con i <confronti con una semplice trasformazione logica : esegui l'incremento del contatore del ciclo subito dopo il confronto, anziché nella parte inferiore del ciclo. IMO questo è più semplice delle alternative che le persone hanno proposto, come usare ++nella prima parte di a for()per fare uno 0 in un 1.
// comments aren't intended as part of the final golfed version
int n;
std::cin >> n; // end condition
for(int r{}; r < n;) { // r = rows from 0 .. n-1
++r;
for(int i{}; i < r;) {
++i;
std::cout << i << ' ';
}
std::cout << std::endl;
}
Potremmo giocare a golf for(int r{}; r++ < n;)ma IMO è meno facile da leggere per gli umani. Non stiamo ottimizzando per il conteggio totale dei byte.
Se stessimo già utilizzando h, potremmo salvare 'o "per uno spazio.
Supponendo un ambiente ASCII o UTF-8, lo spazio è a charcon valore 32. Possiamo crearlo in una variabile abbastanza facilmente, quindicout << c;
char c{};
c++; c++; // c=2
char cc(c+c+c+c); // cc=8
char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8
E altri valori possono ovviamente essere creati da una sequenza di ++e raddoppio, in base ai bit della loro rappresentazione binaria. Spostando efficacemente uno 0 (niente) o 1 (++) nell'LSB prima di raddoppiare in una nuova variabile.
Questa versione utilizza hinvece di 'o ".
È molto più veloce di una delle versioni esistenti (non si basa su un ciclo lungo) ed è privo di comportamento indefinito . Si compila senza avvisi con g++ -O3 -Wall -Wextra -Wpedantice conclang++ . -std=c++11è facoltativo. È legale e portatile ISO C ++ 11 :)
Inoltre non si basa su variabili globali. E l'ho reso più leggibile dall'uomo con nomi di variabili che hanno un significato.
Numero di byte univoci: 25 , esclusi i commenti con cui ho eliminatog++ -E . Ed escludendo spazio e newline come il tuo contatore. Ho usato sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic da questo askubuntu per contare le occorrenze di ciascun personaggio, e ho analizzato questowc per contare quanti personaggi unici avevo.
#include<iostream>
int main() {
char c{};
c++; c++; // c=2
char cc(c+c+c+c); // cc=8
char s(cc+cc+cc+cc); // s=32 = ' ' = space in ASCII/UTF-8
int n;
std::cin >> n; // end condition
for(int r{}; r < n;) { // r = rows counting from 0
++r;
for(int i{}; i < r;) {
++i;
std::cout << i << s;
}
std::cout << std::endl;
}
}
Gli unici 2 fcaratteri provengono da for. Potremmo usare i whileloop invece se ne avessimo bisogno w.
Potremmo eventualmente riscrivere i loop in uno stile di linguaggio assembly i < r || goto some_label;per scrivere un salto condizionale nella parte inferiore del loop, o altro. (Ma usando orinvece di ||). No, non funziona. gotoè un'affermazione simile ife non può essere un sottocomponente di un'espressione come in Perl. Altrimenti avremmo potuto usarlo per rimuovere i caratteri (e ).
Potremmo scambiare fper gcon if(stuff) goto label;invece di for, e entrambi i loop sempre eseguito almeno 1 iterazione così avremmo bisogno di un solo ciclo-ramo in basso, come un normale asm do{}whilestruttura ad anello. Supponendo che l'utente inserisca un numero intero> 0 ...
Digraphs e Trigraphs
Fortunatamente, le trigrafi sono state rimosse a partire da ISO C ++ 17, quindi non dobbiamo usarle al ??>posto del }golf unico per la revisione C ++ più recente.
Ma solo trigrafi specifici: ISO C ++ 17 ha ancora digrafi come :>a favore ]e %>a favore} . Quindi al costo di utilizzo %, siamo in grado di evitare sia {e }, e utilizzare %:per #un risparmio netto di 2 meno personaggi unici.
E C ++ ha parole chiave dell'operatore come notper l' !operatore o bitorper l' |operatore. Con xor_eqfor ^=, puoi azzerare una variabile con i xor_eq i, ma ha più caratteri che non stavi utilizzando.
Current g++ignora già le trigrafi di default anche senza -std=gnu++17; devi usarli -trigraphsper abilitarli, o -std=c++11qualcosa per rigorosa conformità a uno standard ISO che li include.
23 byte unici:
%:include<iostream>
int main() <%
int n;
std::cin >> n;
for(int r<% %>; r < n;) <%
++r;
for(int i<%%>; i < r;) <%
++i;
std::cout << i << ' ';
%>
std::cout << std::endl;
%>
%>
Provalo online!
La versione finale utilizza una 'virgoletta singola anziché ho "per il separatore di spazi. Non volevo digerire la char c{}roba, quindi l'ho cancellata. La stampa di un carattere è più efficiente della stampa di una stringa, quindi l'ho usata.
Istogramma:
$ sed 's/\(.\)/\1\n/g' ladder-nocomments.cpp | sort | uniq -ic | tee /dev/tty | wc -l
15 // newline
95 // space
11 %
2 '
3 (
3 )
4 +
9 :
10 ;
14 <
8 >
2 a
4 c
6 d
3 e
2 f
12 i
2 l
2 m
11 n
5 o
7 r
5 s
11 t
3 u
25 // total lines, including space and newline
Il separatore di spazio (ancora irrisolto)
In una risposta ora cancellata, Johan Du Toit ha proposto di utilizzare un separatore alternativo, in particolare std::ends. È un carattere NUL char(0)e viene stampato come larghezza zero sulla maggior parte dei terminali. Quindi il risultato sarebbe simile 1234, no 1 2 3 4. O peggio, separati da immondizia su tutto ciò che non è crollato silenziosamente '\0'.
Se è possibile utilizzare un separatore arbitrario, quando 0è facile creare la cifra cout << some_zeroed_var. Ma nessuno vuole 10203040, è anche peggio di nessun separatore.
Stavo cercando di pensare a un modo per creare una std::stringholding a" " senza usare charo una stringa letterale. Forse aggiungere qualcosa ad esso? Forse con un digraph per []impostare il primo byte su un valore di 32, dopo averne creato uno con lunghezza 1 tramite uno dei costruttori?
Johan ha anche suggerito la std::iosfunzione membro fill () che restituisce il carattere di riempimento corrente. L'impostazione predefinita per uno stream è impostata da std::basic_ios::init(), ed è ' '.
std::cout << i << std::cout.fill();sostituisce << ' ';ma utilizza .invece di' .
Con -, possiamo prendere un puntatore a coute l'uso ->fill()di chiamare la funzione membro:
std::cout << (bitand std::cout)->fill(). O no, non eravamo utilizzando bsia così potremmo anche usato &al posto del suo equivalente lessicale, bitand.
Chiamare una funzione membro senza .o->
Inseriscilo in una classe e definiscilo operator char() { fill(); }
// not digraphed
struct ss : std::ostream { // default = private inheritance
// ss() { init(); } // ostream's constructor calls this for us
operator char() { return fill(); }
}
Quindi ss s{}prima del loop e std::cout << i << s;all'interno del loop. Fantastico, si compila e funziona correttamente, ma abbiamo dovuto usare pe hper operator char(), per una perdita netta di 1. Almeno abbiamo evitato bdi fare funzioni membro publicusando structinvece di class. (E potremmo scavalcare l'eredità con protectednel caso in cui ciò possa mai aiutare).