Skip to content

Commit bb7aa4b

Browse files
committed
cpp10〜12, 複数ソースファイルをコンパイルする場合の仕様を修正
1 parent 02e986a commit bb7aa4b

File tree

8 files changed

+1007
-12
lines changed

8 files changed

+1007
-12
lines changed

app/pagesList.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export const pagesList = [
3434
{ id: 7, title: "継承とポリモーフィズム" },
3535
{ id: 8, title: "テンプレート" },
3636
{ id: 9, title: "STL ①:コンテナ" },
37+
{ id: 10, title: "STL ②:アルゴリズムとラムダ式"},
38+
{ id: 11, title: "RAIIとスマートポインタ" },
39+
{ id: 12, title: "プロジェクトの分割とビルド" },
3740
],
3841
},
3942
] as const;

app/terminal/exec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export type ExecLang = "python" | "cpp";
1818
interface ExecProps {
1919
/*
2020
* Pythonの場合はメインファイル1つのみを指定する。
21-
* C++の場合はソースコード(.cpp)とヘッダー(.h)を全部指定し、cppRunFiles()内で拡張子を元にソースコードと追加コードが分けられる
21+
* C++の場合はソースコード(.cpp)を全部指定する
2222
*/
2323
filenames: string[];
2424
language: ExecLang;

app/terminal/wandbox/cpp.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,9 @@ export function getCppCommandlineStr(
127127
compilerList: CompilerInfo[],
128128
filenames: string[]
129129
) {
130-
const namesSource = filenames.filter((name) => name.endsWith(".cpp"));
131130
return [
132131
...cppOptions(compilerList).commandline,
133-
...namesSource,
132+
...filenames,
134133
"&&",
135134
"./a.out",
136135
].join(" ");
@@ -142,24 +141,18 @@ export async function cppRunFiles(
142141
filenames: string[]
143142
) {
144143
const options = cppOptions(compilerList);
145-
const namesSource = filenames.filter((name) => name.endsWith(".cpp"));
146-
const namesAdditional = filenames.filter((name) => !name.endsWith(".cpp"));
147144
const result = await compileAndRun({
148145
compilerName: options.compilerName,
149146
compilerOptions: options.compilerOptions,
150147
compilerOptionsRaw: [
151148
...options.compilerOptionsRaw,
152-
...namesSource,
149+
...filenames,
153150
"_stacktrace.cpp",
154151
],
155152
codes: [
156-
...namesSource.map((name) => ({
153+
...Object.entries(files).map(([name, code]) => ({
157154
file: name,
158-
code: files[name] || "",
159-
})),
160-
...namesAdditional.map((name) => ({
161-
file: name,
162-
code: files[name] || "",
155+
code: code || "",
163156
})),
164157
{ file: "_stacktrace.cpp", code: CPP_STACKTRACE_HANDLER },
165158
],

app/terminal/wandbox/wandbox.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export function WandboxProvider({ children }: { children: ReactNode }) {
5656
{ type: "error" as const, message: "Wandbox is not ready yet." },
5757
];
5858
}
59+
console.log(files)
5960
switch (lang) {
6061
case "C++":
6162
return cppRunFiles(compilerList, files, filenames);

public/docs/cpp-1.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ g++ main.cpp -o main
7777
./main
7878
```
7979

80+
```powershell
81+
# Visual C++ (MSVC) を使う場合
82+
cl main.cpp /Fe:main.exe
83+
84+
# 生成された実行可能ファイルを実行
85+
main.exe
86+
```
87+
8088
このウェブサイト上の実行環境で動かす場合は、以下の実行ボタンをクリックしてください。
8189
実行すると、ターミナルに以下のように表示されるはずです。
8290

public/docs/cpp-10.md

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
# 第10章: 標準テンプレートライブラリ (STL) ②:アルゴリズムとラムダ式
2+
3+
ようこそ、C++チュートリアルの第10章へ!前の章ではSTLのコンテナについて学び、様々なデータを効率的に格納する方法を見てきました。しかし、データを格納するだけではプログラムは完成しません。そのデータを並べ替えたり、検索したり、特定の処理を施したりといった「操作」が必要です。
4+
5+
この章では、STLのもう一つの強力な柱である**アルゴリズム**ライブラリを学びます。これらのアルゴリズムは、コンテナ内のデータを操作するための汎用的な関数群です。そして、アルゴリズムをさらに柔軟かつ強力にするための現代的なC++の機能、**ラムダ式**についても解説します。これらをマスターすれば、驚くほど少ないコードで複雑なデータ操作が実現できるようになります。
6+
7+
## イテレータ:コンテナとアルゴリズムを繋ぐ架け橋
8+
9+
アルゴリズムは、特定のコンテナ(`std::vector``std::list` など)に直接依存しないように設計されています。では、どうやってコンテナ内の要素にアクセスするのでしょうか?そこで登場するのが**イテレータ (Iterator)** です。
10+
11+
イテレータは、コンテナ内の要素を指し示す「ポインタのような」オブジェクトです。ポインタのように `*` で要素の値を参照したり、`++` で次の要素に進んだりできます。
12+
13+
ほとんどのコンテナは、以下の2つの重要なイテレータを取得するメンバ関数を持っています。
14+
15+
* `begin()`: コンテナの先頭要素を指すイテレータを返す。
16+
* `end()`: コンテナの**最後の要素の次**を指すイテレータを返す。これは有効な要素を指していない「番兵」のような役割を果たします。
17+
18+
アルゴリズムは、この `begin()``end()` から得られるイテレータのペアを使い、操作対象の「範囲」を指定します。範囲は半開区間 `[begin, end)` で表され、`begin` が指す要素は範囲に含まれ、`end` が指す要素は含まれません。
19+
20+
簡単な例を見てみましょう。イテレータを使って `vector` の全要素を表示するコードです。
21+
22+
```cpp:iterator_example.cpp
23+
#include <iostream>
24+
#include <vector>
25+
26+
int main() {
27+
std::vector<int> numbers = {0, 1, 2, 3, 4};
28+
29+
// イテレータを使ってコンテナを走査
30+
std::cout << "Numbers: ";
31+
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
32+
std::cout << *it << " "; // *it で要素の値にアクセス
33+
}
34+
std::cout << std::endl;
35+
36+
// C++11以降の範囲ベースforループ (内部ではイテレータが使われている)
37+
std::cout << "Numbers (range-based for): ";
38+
for (int num : numbers) {
39+
std::cout << num << " ";
40+
}
41+
std::cout << std::endl;
42+
43+
return 0;
44+
}
45+
```
46+
47+
```cpp-exec:iterator_example.cpp
48+
Numbers: 0 1 2 3 4
49+
Numbers (range-based for): 0 1 2 3 4
50+
```
51+
52+
このように、イテレータはコンテナの種類を問わず、統一的な方法で要素にアクセスする仕組みを提供します。これが、アルゴリズムが様々なコンテナに対して汎用的に機能する理由です。
53+
54+
## 便利なアルゴリズム
55+
56+
C++の標準ライブラリには、`<algorithm>` ヘッダと `<numeric>` ヘッダに数多くの便利なアルゴリズムが用意されています。ここでは、特によく使われるものをいくつか紹介します。
57+
58+
### `std::sort`: 要素を並べ替える
59+
60+
名前の通り、指定された範囲の要素をソートします。デフォルトでは昇順に並べ替えます。
61+
62+
```cpp:sort_example.cpp
63+
#include <iostream>
64+
#include <vector>
65+
#include <algorithm> // std::sort のために必要
66+
#include <string>
67+
68+
int main() {
69+
std::vector<int> numbers = {5, 2, 8, 1, 9};
70+
71+
// numbers.begin() から numbers.end() の範囲をソート
72+
std::sort(numbers.begin(), numbers.end());
73+
74+
std::cout << "Sorted numbers: ";
75+
for (int num : numbers) {
76+
std::cout << num << " ";
77+
}
78+
std::cout << std::endl;
79+
80+
std::vector<std::string> words = {"banana", "apple", "cherry"};
81+
std::sort(words.begin(), words.end());
82+
83+
std::cout << "Sorted words: ";
84+
for (const auto& word : words) {
85+
std::cout << word << " ";
86+
}
87+
std::cout << std::endl;
88+
89+
return 0;
90+
}
91+
```
92+
93+
```cpp-exec:sort_example.cpp
94+
Sorted numbers: 1 2 5 8 9
95+
Sorted words: apple banana cherry
96+
```
97+
98+
### `std::find`: 要素を検索する
99+
100+
指定された範囲から特定の値を持つ要素を検索します。
101+
102+
* **見つかった場合**: その要素を指すイテレータを返します。
103+
* **見つからなかった場合**: 範囲の終端を示すイテレータ (`end()`) を返します。
104+
105+
この性質を利用して、要素が存在するかどうかをチェックできます。
106+
107+
```cpp:find_example.cpp
108+
#include <iostream>
109+
#include <vector>
110+
#include <algorithm> // std::find のために必要
111+
112+
int main() {
113+
std::vector<int> numbers = {10, 20, 30, 40, 50};
114+
int value_to_find = 30;
115+
116+
// numbers の中から 30 を探す
117+
auto it = std::find(numbers.begin(), numbers.end(), value_to_find);
118+
119+
if (it != numbers.end()) {
120+
// 見つかった場合
121+
std::cout << "Found " << *it << " at index " << std::distance(numbers.begin(), it) << std::endl;
122+
} else {
123+
// 見つからなかった場合
124+
std::cout << value_to_find << " not found." << std::endl;
125+
}
126+
127+
value_to_find = 99;
128+
it = std::find(numbers.begin(), numbers.end(), value_to_find);
129+
130+
if (it != numbers.end()) {
131+
std::cout << "Found " << *it << std::endl;
132+
} else {
133+
std::cout << value_to_find << " not found." << std::endl;
134+
}
135+
136+
return 0;
137+
}
138+
```
139+
140+
```cpp-exec:find_example.cpp
141+
Found 30 at index 2
142+
99 not found.
143+
```
144+
145+
### `std::for_each`: 各要素に処理を適用する
146+
147+
指定された範囲の全ての要素に対して、特定の関数(処理)を適用します。ループを書くよりも意図が明確になる場合があります。
148+
149+
```cpp
150+
// 3番目の引数に関数を渡す
151+
std::for_each(numbers.begin(), numbers.end(), print_function);
152+
```
153+
154+
ここで「特定の処理」をその場で手軽に記述する方法が**ラムダ式**です。
155+
156+
## ラムダ式:その場で書ける無名関数
157+
158+
ラムダ式(Lambda Expression)は、C++11から導入された非常に強力な機能です。一言で言えば、「**その場で定義して使える名前のない小さな関数**」です。これにより、アルゴリズムに渡すためだけの短い関数をわざわざ定義する必要がなくなり、コードが非常に簡潔になります。
159+
160+
ラムダ式の基本的な構文は以下の通りです。
161+
162+
```cpp
163+
[キャプチャ](引数リスト) -> 戻り値の型 { 処理本体 }
164+
```
165+
166+
* `[]` **キャプチャ句**: ラムダ式の外にある変数を取り込んで、式の中で使えるようにします。
167+
* `[]`: 何もキャプチャしない。
168+
* `[=]`: 外の変数を全て値渡し(コピー)でキャプチャする。
169+
* `[&]`: 外の変数を全て参照渡しでキャプチャする。
170+
* `[x, &y]`: 変数 `x` は値渡し、変数 `y` は参照渡しでキャプチャする。
171+
* `()` **引数リスト**:通常の関数と同じ引数を取ることができます。
172+
* `-> 戻り値の型`: 戻り値の型を指定します。多くの場合、コンパイラが推論できるため省略可能です。
173+
* `{}` **処理本体**: 関数の処理内容を記述します。
174+
175+
`std::for_each` とラムダ式を組み合わせた例を見てみましょう。
176+
177+
```cpp:for_each_lambda_example.cpp
178+
#include <iostream>
179+
#include <vector>
180+
#include <algorithm>
181+
182+
int main() {
183+
std::vector<int> numbers = {1, 2, 3, 4, 5};
184+
185+
// 各要素を2倍して表示する
186+
std::cout << "Doubled numbers: ";
187+
std::for_each(numbers.begin(), numbers.end(), [](int n) {
188+
std::cout << n * 2 << " ";
189+
});
190+
std::cout << std::endl;
191+
192+
// 外部の変数をキャプチャする例
193+
int sum = 0;
194+
// `&sum` で sum を参照キャプチャし、ラムダ式内で変更できるようにする
195+
std::for_each(numbers.begin(), numbers.end(), [&sum](int n) {
196+
sum += n;
197+
});
198+
199+
std::cout << "Sum: " << sum << std::endl;
200+
201+
return 0;
202+
}
203+
```
204+
205+
```cpp-exec:for_each_lambda_example.cpp
206+
Doubled numbers: 2 4 6 8 10
207+
Sum: 15
208+
```
209+
210+
このコードは、`for_each` の3番目の引数に直接処理を書き込んでいます。非常に直感的で読みやすいと思いませんか?
211+
212+
ラムダ式は、特に `std::sort` のソート順をカスタマイズする際に真価を発揮します。例えば、数値を降順にソートしたい場合、比較ルールをラムダ式で与えることができます。
213+
214+
```cpp:lambda_sort_example.cpp
215+
#include <iostream>
216+
#include <vector>
217+
#include <algorithm>
218+
219+
int main() {
220+
std::vector<int> numbers = {5, 2, 8, 1, 9};
221+
222+
// 比較関数としてラムダ式を渡す
223+
// a > b であれば true を返すことで降順ソートになる
224+
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
225+
return a > b;
226+
});
227+
228+
std::cout << "Sorted in descending order: ";
229+
for (int num : numbers) {
230+
std::cout << num << " ";
231+
}
232+
std::cout << std::endl;
233+
234+
return 0;
235+
}
236+
```
237+
238+
```cpp-exec:lambda_sort_example.cpp
239+
Sorted in descending order: 9 8 5 2 1
240+
```
241+
242+
## この章のまとめ
243+
244+
この章では、STLのアルゴリズムとラムダ式について学びました。
245+
246+
* **イテレータ**は、コンテナの要素を指し示すオブジェクトであり、アルゴリズムとコンテナの間のインターフェースとして機能します。
247+
* `<algorithm>` ヘッダには、**`std::sort`** (ソート)、**`std::find`** (検索)、**`std::for_each`** (繰り返し処理) といった、汎用的で強力なアルゴリズムが多数用意されています。
248+
* **ラムダ式**は、その場で定義できる無名関数であり、アルゴリズムに渡す処理を簡潔かつ直感的に記述することができます。
249+
* **キャプチャ**機能を使うことで、ラムダ式の外にある変数を取り込んで処理に利用できます。
250+
251+
コンテナ、イテレータ、アルゴリズム、そしてラムダ式。これらを組み合わせることで、C++におけるデータ処理は、他の多くの言語に引けを取らない、あるいはそれ以上に表現力豊かで効率的なものになります。
252+
253+
### 練習問題1: 文字列の長さでソート
254+
255+
`std::vector<std::string>` を用意し、格納されている文字列を、文字数が短い順にソートして、結果を出力するプログラムを作成してください。`std::sort` とラムダ式を使用してください。
256+
257+
**ヒント**: ラムダ式は2つの文字列を引数に取り、1つ目の文字列の長さが2つ目の文字列の長さより短い場合に `true` を返すように実装します。
258+
259+
260+
```cpp:practice10_1.cpp
261+
#include <iostream>
262+
#include <vector>
263+
#include <algorithm>
264+
265+
int main() {
266+
std::vector<std::string> words = {"apple", "banana", "kiwi", "cherry", "fig", "grape"};
267+
268+
return 0;
269+
}
270+
```
271+
272+
```cpp-exec:practice10_1.cpp
273+
fig kiwi grape apple banana cherry
274+
```
275+
276+
### 練習問題2: 条件に合う要素のカウント
277+
278+
`std::vector<int>` に整数をいくつか格納します。その後、ラムダ式と `std::for_each`(または他のアルゴリズム)を使って、以下の2つの条件を満たす要素がそれぞれいくつあるかを数えて出力してください。
279+
280+
1. 正の偶数である要素の数
281+
2. 負の奇数である要素の数
282+
283+
**ヒント**: カウント用の変数を2つ用意し、ラムダ式のキャプチャ句で参照キャプチャ (`[&]`) して、式の中でインクリメントします。
284+
285+
```cpp:practice10_2.cpp
286+
#include <iostream>
287+
#include <vector>
288+
#include <algorithm>
289+
290+
int main() {
291+
std::vector<int> numbers = {3, -1, 4, -5, 6, -7, 8, 0, -2};
292+
293+
294+
return 0;
295+
}
296+
```
297+
298+
```cpp-exec:practice10_2.cpp
299+
Positive even count: 3
300+
Negative odd count: 3
301+
```

0 commit comments

Comments
 (0)