4[C入門]

Cでは文字と文字列は違います。'a','3','!'みたいなのが文字。"abc","atcoder","yk0n"みたいなのが文字列です。文字は「'」で囲み,文字列は「"」で囲みます。

文字

文字は数に対応しています。これが文字コードです。

'a'の文字コードは97。'3'の文字コードは51。'!'の文字コードは33。ただし環境によって異なります。

文字はchar型です。char型は必ず1バイトです。

char ch = 'a';
printf("%c", ch);

aと出力されます。文字を出力する専用の関数もあります。putcharです。putcharはすごい簡単で,

putchar(ch);

これでchの表す文字が出力されます。putcharは出力した文字を返します。

putchar('d');

dと出力されます。

putchar(putchar('2') + 3);

25と出力されます。(環境によらない)

文字列

さて,"abc"みたいな文字列を扱いたいときはどうすればよいか。

文字の配列を使うんです。char[]型です。言い換えるとchar*型です。

文字列としてnull-terminated character array(ヌル文字で終わる文字配列)を使うのがC言語の特徴です。

3文字の文字列なら4バイト,10文字の文字列なら11バイト確保して,最後にヌル文字'\0'をつけます。ヌル文字が文字列の終端を表します。これは関数に文字列を渡した時に文字列がどこで終わるのかが分かるようにするための措置です。

「"」で囲うと,文字列の先頭へのポインタを表します。

char buf[4] = "abc"; printf(buf);

abcと出力されます。

char buf[] = "abc"; putchar(buf[1]);

bと出力されます。

「"」で囲った文字列が登場した時点でその文字列のための領域が確保されるので,

char *buf = "abc";

としても全く同じです。

文字列の長さを取得するstrlen関数,文字列を比較するstrcmp関数,文字列をコピーするstrcpy関数,文字列を連結するstrcat関数などいろいろな関数があります。読み方は自由です。strはstringの略。lenはlength,cmpはcompare,cpyはcopy,catはconcatenateの略。昔のC言語は関数(外部識別子)の名前が6文字以内でなければいけなかったので,ぎゅっと省略されています。

文字列の配列

char hoge[5][10] = {"abcdefghi", "jklm", "no", "pqrst", "uvwxyz"};
printf(hoge[0]);
printf(hoge[3]);

長さ9の文字列が5つ。abcdefghipqrstと出力されます。[5][10]の5だけは省略できます。また,char *hoge[5]としても同じです。

*(C入門 + 3)

ポインタ

複数の変数を一度に用意しなければいけないときがあります。そういうときに配列を使うのですが,配列を理解するためにはポインタを理解していないといけません。

int型のaという変数を宣言したとき,メモリ上のどこかに数バイト(環境によるけど4バイトくらい)の領域が確保されます。その場所をアドレスといいます。

アドレスが&演算子で取得できることは説明しました。ここでは,アドレスそのものを変数として扱う方法を紹介します。

int *p;

 とすると,int*型の変数pが宣言されます。int*はintではありません。int型の変数のアドレスの値を格納する変数です(int型以外の変数のアドレスをそのまま格納することはできません)。例えば

int a;
int *p;
p = &a;

 とするとpはaのアドレスを指します。それを参照するときはpの前に*をつけます。pを宣言したときの*とは違うので注意。

あと,int *a, b;とすると,どういうわけかaはint*型,bはint型になるようになっています。int*型の変数を「,」で区切って2つ以上宣言する場合はint *a, *b;としなければいけません。

int a;
int *p = &a;
a = 10;
printf("%d\n", a);
printf("%p\n", &a);
printf("%p\n", p);
printf("%d\n", *p);

printfでアドレスを表示するときは%dではなく%pを使って16進数にするのがマナーです。

実行すると,aの値(10),aのアドレス(unspecified),pの値すなわちaのアドレス(unspecified),pの参照先すなわちaの値(10)が順に表示されます。

ちなみにprintf関数は有能なので

printf("%d\n%p\n%p\n%d\n", a, &a, p, *p);

とまとめて書くこともできます。

ここで,*pはaのエイリアス(別名)です。

ちなみに,pそのものも変数なので,pのアドレスをとることもできます。

printf("%p", &p);

 暇があれば実行してみて下さい。

さらに付け加えると,int*はint型の変数へのアドレスを格納しましたが,int*型の変数へのアドレスを格納するint**も作れます。

int a, *p = &a, **pp = &p;
printf("%d %p %p %d %p %p %d %p %p\n", a, &a, p, *p, &p, pp, **pp, *pp, &pp);

 本当に暇な人はやってみて下さい。

配列

100個の変数を一度に用意したいとき,Cでは配列を使います。

int a[100];

 これでintがメモリ上に連続して100個用意されます。intが一つ4バイトなら全部で400バイトです。

配列の要素にアクセスしたいときはポインタの出番です。

aというのが実は配列の先頭へのアドレスを指すことになっています。よって1つ目の要素にアクセスするときは *a です。

さて,n番目の要素にアクセスしたい。ここで,100個の変数の領域が「連続して」確保されているというのが重要になります。つまりaが19FDBCでintが4バイトなら(今僕のPCでやってみたらこうだった),1つ目の要素が19FDBCのところにあるので,2つ目の要素は19FDC0,3つ目の要素は19FDC4のところにあることになります。なのでポインタに4足して参照すれば2つ目の要素,8足して参照すれば3つ目の要素が出てくるわけです。

ここでポインタと整数の足し算というものがどう定義されているかを知っていなくてはいけません。

int *a = 1000; int b = 10;

 とすると,a+bは実は1040になります(intが4バイトの場合)。配列を扱いやすくするためにそう定義したんです。よって配列のn+1番目の要素にアクセスしたいときは

*(a + n)

 とすればいいんです。そしてこれには別の表記法があります。

a[n]

 は*(a+n)と同じ意味です。これを糖衣構文(シンタックスシュガー)といいます。配列を宣言するときにも[]を使いましたが,ちょっと違うので注意してください。あと足し算はどちらの順番で計算してもよいのでn[a]でも配列aのn番目の要素になります。

注意:配列は少し多めにとっておく(575)。例えば1000個の配列が欲しいときはint a[1024];くらいにするのがよいです。

scanfに渡すときはi番目の要素は a + i (アドレスなので)。

for(int i = 0; i < 10; i++) scanf("%d", a + i);

配列の初期化

int a[10] = {3, 7, 1, 2, 0, 0, 1, 2, 8, 4};

 と書くと,a[0]が3,a[1]が7,a[2]が1,a[3]が2,a[4]が0,a[5]が0,a[6]が1,a[7]が2,a[8]が8,a[9]が4である長さ10の配列ができます。こういうふうに初期化するときは[]の中の10は省略できます。

int a[] = {3, 7, 1, 2, 0, 0, 1, 2, 8, 4}; // 長さ10の配列

malloc

int n;
int a[n];

 みたいなことは実はCではできません。配列を宣言するときの[]の中身は定数式,つまり「3」とか「100」とか「4+3」とかになっていなければいけません(プリプロセッサは大丈夫)。

nが10000以下とか制約がついていればint a[10100]くらいとって最初のn個だけを使っていればいいです。

メモリを節約する場合はstdlib.hのmallocという関数を使います。

int *a;
a = (int *)malloc(sizeof(int) * n);

 説明は面倒なので放棄。malloc(sizeof(型) * 個数)でメモリが確保できるよ。それに(型*)をつけて代入するよ。以上。

嘘です,ちゃんと説明します。別にここだけ読み飛ばしてもいいけど。

malloc(n) で n バイトの領域を確保します。

C言語には型というものがあります。これまでintだけを扱ってきましたが,他にもunsignedやcharやdoubleなどがあります。int*型とdouble*型は違うものとして扱われます。

では,int型の配列の確保にもdouble型の配列の確保にも使われるmalloc関数は何の型を返せばいいのか。

実はvoid*型というものがあるんです。malloc関数はそれを返します。

それをint*型にキャスト(変換)して代入します。キャストしないと怒られます。

キャストの方法は簡単。値の前に(型)とつけるだけ。

よって(型 *)malloc(sizeof(型) * 個数)となります。

mallocで確保した配列は内部的には配列として宣言した配列と同じですが,sizeof演算子の挙動だけが異なります。

int a[10];
int *b = (int *)malloc(sizeof(int *) * 10);
printf("%d %d %d", sizeof(int), sizeof(a), sizeof(b));

僕の環境では「4 40 4」と出力されました。

2次元配列

int a[5][3];

とするとメモリ上にsizeof(int) * 15バイトの領域が確保されます。

ここで

a…先頭へのアドレス

**a (= a[0][0] ) …最初の要素

*a (= a[0] ) … a[0][0] ~ a[0][2] が入ってる配列(長さ3)

a[1] … a[1][0] ~ a[1][2] が入ってる配列(長さ3)

*a[1] … *(a[1]) (= a[1][0] ) (優先順位)

*((int *)a + 5) … a[1][2] (キャスト)

こういうのテストに出やすいから覚えとけよー

初期化するときは

int a[5][3] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};

 とする。このとき[5][3]の5は省略できますが3は省略できません。

int a[][3] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};//はい
int a[5][] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};//いいえ
int a[][] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};//いいえ

mallocやるときはforを使います。

int m, n, **a;
a = malloc(sizeof(int *) * m);
for(int i = 0; i < m; i++) a[i] = malloc(sizeof(int) * n);

 これマクロにしておくと便利だけど使ってる人見たことないな…。

同じようにして3次元配列とか4次元配列とかも作れるけど滅多に使いません。

C入門[2]

if文,for文,while文その他について簡単に説明

条件分岐
if(①) 中身;

①が真(=0でない)ならば実行される。

if(①) 中身1;
else 中身2;

 ①が真ならば中身1が,偽(=0に等しい)ならば中身2が実行される。

switch(⓪){
case ①:
    中身1;
case ②:
    中身2;
    :
default:
    中身0;
}

⓪,①,②,…はintっぽいもの。 

⓪が①に等しければ中身1から,②に等しければ中身2から,…実行される。どれにも等しくなければdefault:から実行される。breakを忘れがち。あと古いCだと2つめ以降で変数を宣言しようとするとエラーが出る。ブロックにすれば問題無し。

① ? ② : ③

 excelのif関数みたいなやつ。①が真なら②,偽なら③の値になる。優先順位が割と低い演算子

for(①; ②; ③) [中身];

 最初に①が実行されて,②が真の間は中身と③が実行される。gotoを使って作るとこんな感じ:

    ①;
L1:
    if(②) goto L2;
    中身;
    ③;
    goto L1;
L2:

実際中ではこんな感じになってる。

REPマクロ

#define REP(i, n) for(int i = 0; i < n; i++)

とその親戚(RREP(降下),FOR(a to b)など)は便利。erはみんなやってる。 

while(①) [中身];

①が真の間中身が繰り返し実行される。

do [中身] while(①);

中身を実行した後に①が判定される。

中身が2~3行でブロックを作るのが面倒なときは「,」で区切るのもあり。

 こんなもんかな。(やる気のNASA)

C入門[1]

今回はちょっと詳しくやるので長くなります。

 

標準出入力

CUIプログラムは,入力に応じて出力をします。

このときの入力を標準入力といい,出力を標準出力といいます。

コマンドプロンプトで普通に実行したときは,ユーザの入力が標準入力で,プログラムによってコマンドプロンプトに表示されるのが標準出力です。

 

標準出入力を特定のファイルにリダイレクトすることもできます。

コマンドプロンプトからプログラムを呼び出すときに,プログラム名の後に続けて「>~.txt」とファイル名を指定すると,出力先がそのファイルになります。「0<~.txt」とするとそのファイルから読み込みます。

 

Hello, world!

プログラミングを勉強する人が必ず最初に作るプログラムがあります。

その名もHello World。標準出力に「Hello, world!」と出力するプログラムです。

とりあえずそのコードを出して軽く説明します。

#include <stdio.h>
int main(void){
    printf("Hello, world!\n");
    return 0;
}

 これをコンパイルして実行すると標準出力に「Hello, world!」と出力されるはずです。

このプログラムについて説明します。

「main関数」と呼ばれる関数がプログラムの本体になります。

関数とは,①いくつかの引数ひきすう(0個でもよい)を受け取って,②何かをして,③値を返すものです。

int main(void) は「main関数の中身を続く{ }の中に書くよ!」という意味です。「void」は引数が0個である(引数をとらない)ことを表し,「int」は返す値が int 型であることを表します。型についてはまたいつか書きます。

main関数の中身は続く{ }の中に書かれています。ここでは{ }の中に2つの文があります。「printf("Hello, world!\n");」と「return 0;」です。文の終わりには必ずセミコロン「;」を入れます。慣れていないうちは忘れやすいので注意。

printfは関数です。「printf(文字列)」で文字列を渡してprintf関数を呼び出すと,標準出力に文字列が出力されます。ただしそのまま出力されるわけではありません。「\n」は改行として扱われます。つまり,「printf("Hello, world!\n");」で「Hello, world!」と出力して改行するという意味になります。printf関数は出力した文字数(バイト数)を返しますがここでは使いません。

printf の f はフォーマットの f です。これについてはまたいつか書きます。

return は関数の終わりを表します。main関数の中に書かれているこのreturn文はmain関数の終了,すなわちプログラムの終了を意味します。return に続く 0 は,main関数に0という値を返させます。0を返すのはマナーです。別に「return 1;」でも正常に実行されます。

さて,まだ説明していないものが一つ残っています。1行目の「#include <stdio.h>」です。これは,printf関数を使うために書かなければいけないものです。

存在しない関数を呼び出すことはできません。呼び出せる関数は,既に「宣言」と「実装」がなされた関数のみです。しかしprintf関数は上のコードの中では宣言も実装もされていません。実は他のファイルの中で宣言されているのです。それが stdio.h というファイルです。これをヘッダファイルといいます。Cのコンパイラをインストールすると,たくさんのヘッダファイルがついてきます。そのうち,使うヘッダファイルだけを #include して使います。printf関数はまた別のファイルの中で実装されています。これをライブラリといいます。競プロerは自分で作った役に立つ関数をコピペして使うときにその関数群をライブラリと呼びますが,本来の意味ではありません。

 C言語において,「#」で始まる行はプリプロセッサといい,コンパイラが一番最初に処理します。

あと int main(void) が int main(int argc, char *argv[]) になることもありますが,競プロでは出てきません。

 

2倍する

問題:標準入力で整数Nが与えられる。Nの2倍を出力せよ。

制約:-100000 ≦ N ≦ 100000

これを満たすプログラムを作るために知っておかなければいけないことが3つあります。

①変数

②標準入力から読み込む方法

③標準出力に数を出力する方法

①変数とは,メモリ上に値を保存するものです。変数には型があります。今回はNが整数なのでint型を使います。intの大きさは決まっていませんが基本4バイト。4バイト=32ビットなので正負9桁くらいが扱えます。

Cでは宣言されていない変数を使うことはできません。変数を宣言するには,型につづけて変数名を書きます。int型の変数Nを宣言するには,

int N;

 とします。これでメモリ上に4バイトの領域を確保できました。この領域は{ }で囲まれたブロック内で自由に使うことができます。メモリ上の変数の場所をアドレスといい,変数の名前の前に&(アンパサンド)をつけて取得することができます。

②で標準入力から数を読み込んでNに格納して,その後にNの値を2倍するのですが,そこはちょっと飛ばして,先に③を説明します。標準出力にNの値を出力します。

文字列でなく数を出力するときもprintf関数を使います。

printf("%d\n", N);

 これでNの値が出力されます。printf関数の1つめの引数は"%d\n"ですが,%dは10進法でNの値に置き換えられます。これがprintfのfがフォーマットたる所以です。

printf("Nの値は%dです\n", N);

とすると,"Nの値は%dです\n"の%dがNに置き換えられ,「Nの値は~です」とNの値が出力されます。

これをちょっと詳しく説明します。

関数を呼び出すときのプロセスは,

レジスタ/スタックに引数を格納する

②呼び出す

の2段階です。スタックは一時的に変数を格納しておく場所です。printf("%d\n", N)と書くと,スタックにNの値と"%d\n"という文字列が格納され,そのあとにprintf関数が呼び出されます。printf関数は,"%d\n"という文字列を読み込み,その中に%dを発見すると,スタックからNの値を読み込み,10進法に変換して,表示します。ここで重要なのは,スタックに格納されているのはNの「値」だということです。Nそのものではありません。

それを踏まえて,②の説明をします。標準入力から数を読み込むときは,scanf関数を使います。scanf関数はstdio.hで宣言されています。

scanf("%d", &N);

これで標準入力から数を読み込んでNに格納できます。今回はNの前にアンパサンドが付いています。その理由を説明します。scanf関数はprintf関数と形式がちょっと似ていますが,決定的な違いがあります。それは,printf関数と違ってscanf関数はNの値を書き換えるということです。scanf関数がNの値を書き換えるために必要なものはなんでしょうか。

もし,printf関数のときのようにスタックに「15」という値が格納されていても,それを読んだscanf関数はNの値を書き換えることができません。

scanf関数がNの値を書き換えるために必要なものは,Nのアドレスです。

スタックにNのアドレスが格納されていれば,scanf関数はそれを書き換えることができます。それで,scanf関数の第2引数はポインタ型なのです。

 

最後に,Nの値を2倍する方法を言います。

N = N * 2;

「=」は代入を表します。右辺を計算してから左辺に代入します。

N *= 2;

と書くこともできます(糖衣構文)。

 

これら全てを組み合わせ,最終的に出来上がるコードがこちら。

#include <stdio.h>
int main(void){
    int N;
    scanf("%d", &N);
    N *= 2;
    printf("%d", N);
    return 0;
}

3行目で変数Nを宣言し,4行目で標準入力から読み込み,5行目でNの値を2倍し,6行目で出力しています。

 

最後に一つ付け加えておきます。BorlandのCコンパイラ

#include <stdio.h>
int main(void){
    int a = 5;
    printf("%d", a);
    printf("%d");
}

とすると,1回目にprintf関数を呼び出したときにスタックにプッシュした5という値が,2回目にprintf関数を呼び出したときもまだ残っているため,全体で「55」と出力されることがあります。gccやclangなどのしっかりしたコンパイラではこのようなことは起こりません。

C入門[0]

新しく入ってくる後輩にプログラミングを教えなければいけないので,そのための資料を作っておきます。

 

C入門,第0回。CUIGUIについて

 

プログラムには2種類あります。CUIプログラムとGUIプログラム。

あなたが普段使っているのはGUIの方です。WordやExcelInternet ExplorerGoogle Chrome,全部GUIです。ゲームも全部GUIです。ではCUIとは何なのか。

Windowsキー+「R」を押して下さい。画面左下に「ファイル名を指定して実行」が出てきます。名前の欄に「cmd」と入力してEnterを押してください。黒い画面が出てきます。これがコマンドプロンプトです。

コマンドプロンプトに「dir/s」と入力してEnterを押してください。

なんか止まらなくなります。

はい。こういうのがCUIです。

(ちなみにこれは「【厨二】誰でもハッカーっぽい画面を表示できる方法」という動画にあったものです。)

つまり,CUIとは「入力された文字列に応じて,何か文字列を出力するプログラム」です。そこには画像も無ければ,ボタンもありません。ただ黒い画面に白い文字が表示されるだけです。

C言語を勉強する人は,まずCUIから始めます。GUIは難しいからです。ちょっと例を挙げます。

例1) CUIで入力された数の2倍を出力するプログラム

#include <stdio.h>
int main(void){
 int a;
 scanf("%d", &a);
 printf("%d", a * 2);
 return 0;
}

例2) GUIでエディットボックスに入力された数の2倍を表示するプログラム

#include <stdio.h>
#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE, PSTR, int){
	WNDCLASSEX WndClassEx;
	WndClassEx.cbSize = sizeof(WNDCLASSEX);
	WndClassEx.style = CS_HREDRAW | CS_VREDRAW;
	WndClassEx.lpfnWndProc = WndProc;
	WndClassEx.cbClsExtra = WndClassEx.cbWndExtra = 0;
	WndClassEx.hInstance = hInstance;
	WndClassEx.hIcon = LoadIcon(0, IDI_APPLICATION);
	WndClassEx.hCursor = LoadCursor(0, IDC_ARROW);
	WndClassEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	WndClassEx.lpszMenuName = 0;
	WndClassEx.lpszClassName = TEXT("Main");
	WndClassEx.hIconSm = LoadIcon(0, IDI_APPLICATION);
	if(!RegisterClassEx(&WndClassEx)) return 0;

	HWND hwnd = CreateWindow(
		TEXT("Main"), TEXT("satiseni"),
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);
	if(hwnd == NULL) return 0;

	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
	static HWND a, b;
	switch(msg){
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		a = CreateWindow(TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT, 0, 0, 200, 30, hwnd, (HMENU)1, ((LPCREATESTRUCT)(lp))->hInstance, NULL);
		b = CreateWindow(TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT, 210, 0, 200, 30, hwnd, (HMENU)2, ((LPCREATESTRUCT)(lp))->hInstance, NULL);
		return 0;
	case WM_COMMAND:
		if(LOWORD(wp) == 1 && HIWORD(wp) == EN_UPDATE){
			char buf[64];
			GetWindowText(a, buf, 63);
			int a = atoi(buf);
			sprintf(buf, "%d", a * 2);
			SetWindowText(b, buf);
		}
		return 0;
	default:
		return DefWindowProc(hwnd, msg, wp, lp);
	}
}

 はい。これがCUIGUIの違いです。GUIの方はいろいろなことが長々と書かれていますが,実はそのほとんどがウィンドウを表示する機構に関するもので,実際に2倍を計算しているのは54行目だけです。つまりウィンドウを表示するというのはそれだけ面倒な作業だということです。

まあ,慣れてしまえば60行のプログラムだってそんなに長く感じないんですがね。

 

では次回(第1回)から実際にプログラムを作る方法を書いていきます。

競プロ用語

twitter上の競プロerの文化や,erが使っている用語を紹介しようと思います。

・色

競プロのコンテストに出場すると,成績に応じてレートが変動します。レートは色分けされています。

例えばatcoderでは

レート
2800以上
オレンジ 2400以上
黄色 2000以上
1600以上
水色 1200以上
800以上
茶色 400以上
灰色 0以上

と色分けされています。サイトによって少しずつ違いますが,どのサイトでも赤が一番強いというのは共通しているようです。

「○○コーダー」というのはこの色を指します。例えばレートが2000以上2400未満の人を「黄コーダー」と呼びます。

twitterのアイコンを自分の色に合わせておくと,どのくらい強いのか見た人がすぐに分かるので便利です。

赤コーダーの人が自分のtwitterアイコンをあえて灰色にすると,ハラスメント(後述)になります。

・リプライ

強いer同士の間では意味の分からないリプが常時飛び交っています。初めのうちは理解しようとしなくてもよいでしょう。いつか分かるときが来ます。

・ハラスメント

 良い成績を取った人が「自分は頭が悪いので~」などと言っているとイラっとしませんか。このように謙遜によって相手に精神的攻撃を加える行為を「ハラスメント」と呼びます。また,格下の相手を過度に褒め称える行為もハラスメントに含まれます。

強いerはよくハラスメントをします。しかしこれは,ただ相手にダメージを与えて楽しんでいるわけではありません。ハラスメントをされた人が「なにくそ」と思って這い上がって来るのを待っているのです。つまりハラスメントは一種の優しさです。

ハラスメントをされたら,「自分は期待されているんだ」と思ってがんばりましょう。その努力はきっと報われます。

・なぞなぞ

一部のerは「クソなぞなぞ」をします。

「<クソなぞなぞ> ~~ってな~んだ?」の形式で出題され,リプで回答します。

その多くがくだらないダジャレによるものですが,このようななぞなぞを解くことは,人が何を言いたいのかを推察する能力の訓練となります。マヨ子さんという方とDEGwerさんという方がよくやっているので,見かけたら取り組みしましょう。

・575

俳句。それは,13文字で心を表現する日本の文化。

競プロerは,どうでもいいことをなぜか575調でツイートします。見かけたら「575」とリプして指摘しましょう。また,575の指摘に慣れ過ぎるあまり,「575」という指摘に対して「5」(「ごーしちご」が5音節なので)とリプを返すerもいるようです。そうしたら,「ご」は1音節なので,「1」と返しましょう。きっと「2」というリプが返ってきます。

typingwar

http://typingwar.trap.games/

 タイピングゲーム。タイピング速度がレートに比例するという迷信があり,多くのerがこのゲームをやっています。

用語

競プロerの使う独特の用語をいくつか紹介しておきます。

「~完」…コンテストで何問完答したかを表す。「3完」で3問完答したという意味。この言い方は競プロに限らず,数学の試験においても使われているようである。

「プロ」…相手を褒める言葉。褒めるとはすなわち煽ることである。誰かが「~ができた」と呟いたらすかさず「プロ」とリプを飛ばすべし。また,「プロ」と言われたら,挨拶だと思って「趣味」と返すのがよい。

「はい」「いいえ」…感動詞。「はい」は肯定,「いいえ」は否定を表す。ネタツイの最後に「(いいえ)」と付けることで,それがネタツイであることを明確にすることができる。当たり前のことを言うときに最後に「(はい)」と付けることで,それが当たり前であることを明確にすることができる。「プロ」「趣味」と組み合わせて「はいプロ」「いいえ趣味」と使うこともできる。

「太陽」…0完,即ち一問も完答できなかったことを華々しく報告する際に使う言葉。「太陽をキメる」「太陽が生える」などと使う。

「罠」…一見正しいかのように思えるが実は間違っていること。間違ったことを言ってしまったとき,「これは罠で…」と訂正する。ネタツイとしてあえて間違ったことを言うときには,最後に「(これは罠)」とつけることで誤解を防ぐことができる。

「実質」…事実を誇張するときに使う。例えば,コンテストで簡単な問題が2問,難しい問題が3問出題されたとき,本当は3完したがそのうち2問はやるだけだったので実質1完,のように使う。誰かが「太陽」と言ったときは,その前に「実質」が省略されていることが多い。

「任意」…数学用語。「頭が悪いので任意の問題が解けない」とは,「どんな問題Pに対しても,『私はPが解けない』が成り立つ」,つまり「どんな問題も解けない」という意味である。「私以外の任意の人間がプロ」「任意のプロの成長速度が速い」など。

「頭」…頭脳。「頭がついていない」は,頭が悪いという意味。「頭が欲しい」は,頭が良くなりたいという意味。

「殴る」…やや強引に物事を解決すること。「頭で殴る」(頭脳を使ってやや強引に物事を解決する),「セグ木で殴る」(セグメントツリーでやや強引にACする)など。

「うぃーん」…擬音語。歩く音,目を閉じる音,エゴサする音,ACする音など,何にでも使える万能の擬音語である。

「†」…ダガー。「†○○†」のように使い,○○の部分には悪魔的行為が入る。「†グローバル変数†みたいなウンコード」(C++グローバル変数をむやみに使ってはいけない)など。

「キャッ///」…キャッキャッ///ハラスメントは楽しいな!!キャッキャッ////

「ア」…死。「~なのでア」「~してしまった(ア」など。

 

DEGwer構文…「はいプロ 世界一○○が上手 ○○界のtourist ○○時代の終焉を告げる者 実質○○ ○○するために生まれてきた男」の空欄を埋めることによって相手を煽る構文が完成する。touristはベラルーシ人の競プロer(強い)。例えば「起きた」というツイートに対して「はいプロ 世界一起床が上手 起床界のtourist 布団時代の終焉を告げる者 実質朝 起床するために生まれてきた男」などと使う。

双子構文…「僕は永遠にあなたの○○を抜かせません。どのようにして○○をしているのか教えていただけないでしょうか」と煽る。

「うぃーんビートビートひるどwwwwwwうっくっくwwwwwwえいえいえt(←いずらいt)いえいwwwwらて。」…コピペ。とりあえずうぃーんビートビートひるどwwwwwwうっくっくwwwwwwえいえいえt(←いずらいt)いえいwwwwらて。というリプを飛ばせばうぃーんビートビートひるどwwwwwwうっくっくwwwwwwえいえいえt(←いずらいt)いえいwwwwらて。なのでうぃーんビートビートひるどwwwwwwうっくっくwwwwwwえいえいえt(←いずらいt)いえいwwwwらて間違いなし。うぃーんビートビートひるどwwwwwwうっくっくwwwwwwえいえいえt(←いずらいt)いえいwwwwらて。の正しい発音の仕方はこちらで紹介されています。

私も練習してみましたが,「いえい」と「らて」のギャップが難しいようです。

 

これらの用語を使いこなせるようになるまでには時間がかかります。また,言語は生き物ですから,これらの用語も刻一刻と変化してゆきます。ただ,競プロの世界に飛び込んでいったとき,何も知らないのと基本的な単語を知っているのでは大きな差が生まれます。この記事が少しでもあなたの役に立てればと思います。

競プロ

学校でパソコン同好会があり,僕はそのプログラミング部門で後輩に C/C++ を教えています。人にものを教えるというのは結構難しいんですね。

 

人にプログラミングを教えるにあたって最も苦労したのが「何を作ればいいのか分からない」ということ。

プログラムは書いた分だけ上手くなります。逆に言うと,自分で書かないと上達しません。

僕が小学生のときにプログラムが上達しなかったのも,作るべきものが特に無かったからです。

 

同好会でも,後輩たちはこれまで何をするともなく適当にコードを書いているだけでした。(ちなみに僕は自分が書いたバグだらけのコードを頑張って修正していました)

 

とにかく何かテーマが欲しい。何を作ればいいのかを提示して欲しい。

 

そんなとき僕が出会ったのが,競技プログラミングでした。

 

競技プログラミング。略して競プロ。

正しいプログラムをいかに早く書けるかを競う競技。

 

atcoder(https://atcoder.jp/) や codeforces(http://codeforces.com/)などのサイトで定期的にコンテストが開催されています。

例えば,「入力されたいくつかの数のうち,1が何個あるかを出力せよ」という問題が出たら,数を1つずつ読み込んで1の個数を数えるようなプログラムを書いて提出します。提出したら即座にジャッジ(正誤判定)が始まり,いくつかのテストケースが実行されます。プログラムが全て正しい出力をしたら「AC」(accepted),出力が間違っていたら「WA」(wrong answer),プログラムが重くて実行に時間がかかりすぎたら「TLE」(time limit exceeded)と表示されます。ACすると得点を得ます。

一回のコンテストでいくつかの問題が出題され,得点の合計を競います。

 

僕は今年1月に競プロを始め,その効果に驚きました。 

 

競プロは実用的なプログラミング力をつけるのに適しています。

自分の書きたいものを書くより,与えられた問題を解くほうが,すぐに力がつきます。

 

というわけで,今年度からは同好会で競プロをやることにしたのです。

 

 

競プロをやる人のことを「競プロer」と呼びます。僕は単に「er」*1と呼びます。

日本人のerは,twitter上にたくさんいます。(僕が競プロを知ったのもtwitterを始めてからです)

twitter上のerは不思議な日本語を使います。「プロ」「はい」「いいえ」「これは罠」「太陽」などの言葉の使い方や,括弧の使い方が特徴的です。

また,過度な謙遜による煽りが多い世界なので,強くなるまでは会話に参加することができません。

 

ここら辺の詳しいことに関しては,また改めて記事を書きたいと思います。

 

ではまた。

 

…というかこの記事,書き方が下手だな。誰だこれ書いたやつ。

*1:口を半開きにして,「あー」と発音します。