Cを踏み台にして,C++を覚えた男の妄言.

この記事はMuroran Institute of Technology Advent Calendar 2018 の23日目です.


adventar.org


 

 皆さん始めまして,所属する大学の1年にAtCoderの布教をしまくってるsakaki素人です.なお,つい最近茶色になったばかりです.(2018/12/23現在)

 さて,タイトルでお察しの通り,Cでプログラミングしている人にC++をオススメする内容です.コーディングが短くなったり楽になったり,便利な機能がたくさん追加されてたりします.そのごく一部を紹介するので,是非C++に浮気しましょう!(競技プログラミングの目線が過分に含まれています.)

 

AtCoder

 C++の良さを紹介するために,このサイトから良さそうな問題を選び,紹介します.

atcoder.jp


 

標準入出力

 

 C言語だとprintfやscanfなどです.

 

#include<stdio.h>
int main(){

   int a,b,c;

   scanf("%d %d %d",&a,&b,&c);

   printf("%d %d %d",a,b,c);

   return 0;

}

 超初歩的なやつです.a,b,cを読み取ってただ表示するだけ.これをC++で書くとこうなります.

#include <iostream>

int main(){
	int a, b, c;

	std::cin >> a >> b >> c;

	std::cout << a << " " << b << " " << c;

	return 0;
}

 using namespace std;というのを入れてあげると,std::とかいうよくわからないのを使わなくて済みます.(ちゃんと勉強するとstd::の重要さに気付くらしいです.なおsakaki素人はまだわかってない.)

#include <iostream>
using namespace std;
int main(){
	int a, b, c;

	cin >> a >> b >> c;

	cout << a << " " << b << " " << c;

	return 0;
}

 少し短くなりましたねえ…この程度しか短くならないのかと思うかもしれませんが,フォーマット指定子がないというのは非常に楽です. (sakaki素人は%fと%lfの違いがわからず,いつもCEします.)

 さて,個人的にCで書くのがつらいのでここからはC++ソースコードのみとなります.ご容赦ください.また,AtCoderさんお世話になりますm(__)m.


atcoder.jp

これをCで解こうとすると,

  • アルファベットの数の分(a~z)だけ配列を用意
  • char型に入れた文字列からaを見つけたらアルファベットa用の配列に+1...をzまで繰り返す
  • アルファベット用の配列が全て2で割り切れるか調べる

こんな感じでしょうか.正直やってらんないです.コード書きたくないです.

というわけでC++のコードを見てみましょう.

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
using namespace std;      //std::というよくわからないやつを消せる.
 
int main(void) {
	string w; cin >> w;       //string型,char型の上位互換だと思ってる.
	map<char, int> mp;       
	int i;
	for (i = 0; i < w.size(); i++) {
		mp[w[i]] += 1;       
	}
 
	for (pair<char, int> x : mp) {
		if (x.second % 2 != 0) {
			cout << "No" << endl; return 0;
		}
	}
 
	cout << "Yes" << endl;
	return 0;
}

 string?map?pair?なんか見知らぬ謎な型名が出てきましたね.でもこれ,便利なんです.

string型.
 char型だとchar a[5]みたいにして要素数を宣言しないといけませんでしたが,要素数が要らなくなりました!おかげで最後に\0(NULL文字)が入ることとかを考えずに済みます.
map.
 何言ってんだこれって感じですよね.
pair.
 これも何言ってるんだって感じですね.言葉の意味的に2つを何かするのかな?って感じですが.

 string型のwを宣言し,wの要素1つ1つをmap型のmpに入れて,値をインクリメントする.値が2で割り切れなかったらNo.割り切れたらYes.このコードは,mapとpairと拡張for文の使い方がわからないと解読できません.これについては書こうとするともう1つブログを書く必要がありますね(宿題発生).多分今度書きます.

 さて,mapを覚えたsakaki素人君は,調子に乗って次の問題をmapでやろうとしました.しかし,All Clearにはなりませんでした.

beta.atcoder.jp

ソースコード

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
using namespace std;
 
int main(void) {
	int n, l; cin >> n >> l;
	int i;
	map<string, int> mp;
	string s;
 
	for (i = 0; i < n; i++) {
		cin >> s;
		mp[s] = 0;
	}
 
	for (pair<string,int> x:mp) {
		cout << x.first;
	}
	return 0;
}

 sakaki素人君は重大な勘違いをしていたのです.彼はこう考えました.
 map<string,int>を宣言し,string型を入れると,mapの内部ではstringの部分がソートされるので,それを出力すれば良いだろう,と.
 しかし,map<string,>の左側は同じ値が入らないということを理解できていなかったのです.以下のソースコードを試せばなんとなく察しがつくと思います.

#include <iostream>
#include <string>
#include<map>

using namespace std;

int main(){
	string s;
	int a;
	map<string, int> mp;
	int i; 
	for (i = 0; i < 5;i++) {//5つの要素を入れるぞ!!
		cin >> s;
		cin >> a;
		mp[s] = a;
	}

	cout << endl;

	for (pair<string, int> x : mp) {
		cout << x.first << " " << x.second << endl;
	}

	return 0;
}

入力
sakaki 5
sakaki 4
Sakaki 3
SAKAKI 2
Sakaki 1

出力
SAKAKI 2
Sakaki 1
sakaki 4

 この問題ではmapは使えませんでした.string型をソートしましょう.これもC++には素晴らしい機能がついてます.

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
 
int main(void) {
	int n, l; cin >> n >> l;
	int i;
	string s[n];
 
	for (i = 0; i < n; i++) {
		cin >> s[i];
	}
 
	sort(s, s + n);
 
	for (i = 0; i < n; i++) {
		cout << s[i];
	}
	return 0;
}

 sortという名の通り,ソートするための関数である.始まりと終わりを指定するだけで勝手にソートしてくれる.(しかも普通にソートのコード書くより早い.)Cだとstrcmpとか使って比較するんでしょうか.とにかく便利です.

まとめ的な何か

 Cにはない様々な便利機能が,C++にはあります.うまく使いこなすには精進が必要ですが,一度使えるようになると面白いようにコードが書けるようになります.(なぜがC++を勉強し始めたらCがわかってきた不思議.)今回出てきたpairやmapなどはまたの機会に書くので,そちらではちゃんとお話しします.
 今回はC++への浮気を推奨するために書いたので,使われた関数の説明などは一切なかったのですが,ソースコードはCで書こうとするよりすっきりしているのではないでしょうか.

最後に

 AtCoderやろうぜ!!!!!!!!!!