[データ構造編]非プログラミングエンジニアがデータ構造の基礎から学び直してみた!
以前私は、Kubernetesの基礎を学び、こちらに記事にしたのですが、これがかなり自分でも見返すのに役に立ったので、今回は、プログラミングにおけるデータ構造の基礎を学びなおしてみたので、同じように学んだことを書いて置こうと思います!
この記事の目次
非プログラミングエンジニアがデータ構造の基礎から学び直してみた!
ハードウェアの整備やネットワーク、セキュリティなど主にインフラエンジニアなどとして仕事を続けてきた私ですが、ここ最近、開発チームと話す中で「データ構造」や「配列」「リスト」「Map」などの言葉に触れる機会が増えてきました。しかし、正直言うとそれぞれの概念をぼんやりとしか理解しておらず、さらにコードを書けるほどの知識は皆無でした。インフラ系の作業においても、サーバーの自動化や効率的なデータ管理にはプログラミングが役立つと実感しており、せめてデータ構造の基礎だけでも押さえようと一念発起しました。今回はそんな非プログラミングエンジニアの私がデータ構造の基礎から学び直した体験について共有したいと思います。
そもそもデータ構造とは!?
まず初めに取り組んだのは、「データ構造」とは一体何なのかという疑問を解消することでした。ざっくり言えば、データ構造は「データをどう整理し、どう扱うか」の方法や形式を指します。例えば、大量のデータを効率的に検索したい、素早く並べ替えたいといった場面で「データ構造」を適切に選べば、処理が格段にスムーズになるのです。プログラミングの中でデータ構造は基盤となる部分なので、まずはここを理解することで、データを使った処理の基本が見えてくるはずです。
最も基本的なデータ構造「配列」Array
プログラミング初心者にとって、最も基本的で理解しやすいデータ構造の一つが「配列(Array)」です。配列は、同じ型のデータを並べて管理できるリストのようなものです。インフラの世界でいうと、配列は複数のIPアドレスやファイルパスをまとめて一つのリストとして扱うイメージです。
例えば、特定のサーバーにあるログファイルを順番に処理するときに、ファイルパスを配列に格納してループで処理すれば、手動で一つずつ指定するよりも遥かに楽になります。
下記のサンプルコードでは、$filePaths
配列に複数のファイルパスを格納してし、ループ処理であるforeach
ループを使って、配列内の各ファイルパスを1つずつ取り出して処理しています。次にfile_exists()
関数でファイルが存在するか確認し、存在する場合はファイルの内容を取得して表示します。
このコードを実行すると、各ファイルの内容が表示され、存在しないファイルについてはエラーメッセージが出力されます。
<?php
// ファイルパスを配列に格納
$filePaths = [
'/path/to/file1.txt',
'/path/to/file2.txt',
'/path/to/file3.txt'
];
// 配列内の各ファイルパスをループで処理
foreach ($filePaths as $path) {
// ファイルが存在するか確認
if (file_exists($path)) {
// ファイルを読み込む処理
$content = file_get_contents($path);
echo "File path: $path\n";
echo "Content:\n$content\n\n";
} else {
echo "File not found: $path\n";
}
}
リスト(List)の柔軟さ
配列に慣れてきたところで、次に取り組んだのが「リスト」です。配列とリストは似ているようで、実は異なる点がいくつもあります。
リストは配列と違ってサイズを気にせず、要素を動的に追加・削除できるのが特徴です。この「動的」という言葉がポイントで、例えばインフラの設定管理ツールでサーバーのIPリストやインストールするパッケージ一覧など、定義が頻繁に変更されるリストに動的に設定を追加・削除するような場面では、この「リスト」の概念が生きてきます。リストに追加や削除を行うことで、環境構築のコードを動的に変えずに、簡単に構成の変更ができるようになります。
<?php
// 配列の作成
$fruits = ["りんご", "ばなな", "みかん"];
// 配列の表示
echo "初めの配列: ";
print_r($fruits);
// 配列に要素を追加
$fruits[] = "ぶどう"; // 新しい要素を末尾に追加
echo "要素を追加した後: ";
print_r($fruits);
// 配列から要素を削除
$index = array_search("ばなな", $fruits); // "ばなな"のインデックスを取得
if ($index !== false) {
unset($fruits[$index]); // 要素を削除
}
echo "要素を削除した後: ";
print_r($fruits);
// 配列の最初の要素を変数に割り当て
list($firstFruit, $secondFruit) = array_slice($fruits, 0, 2); // 最初の2つの要素を取得
echo "最初の果物: $firstFruit\n";
echo "2番目の果物: $secondFruit\n";
// 配列の要素をループで表示
echo "残っている果物:\n";
foreach ($fruits as $fruit) {
echo $fruit . "\n";
}
// 配列の長さを表示
echo "配列の長さ: " . count($fruits) . "\n";
?>
実行結果
初めの配列: Array
(
[0] => りんご
[1] => ばなな
[2] => みかん
)
要素を追加した後: Array
(
[0] => りんご
[1] => ばなな
[2] => みかん
[3] => ぶどう
)
要素を削除した後: Array
(
[0] => りんご
[2] => みかん
[3] => ぶどう
)
最初の果物: りんご
2番目の果物: みかん
残っている果物:
りんご
みかん
ぶどう
配列の長さ: 3
実行結果の説明
1.初めの配列: 最初に作成した配列$fruitsには、りんご、ばなな、みかんが含まれています。
2.要素を追加$fruits[] = "ぶどう";
した後: ぶどうを追加した結果、配列に新しい要素が加わったことが確認できます。
3.要素を削除unset
した後: ばななが削除された後の配列の状態が表示されます。この際、インデックスがずれているため、1が削除され、配列のインデックスは0、2、3になります。
4.最初の果物と2番目の果物: list()関数を使って、最初の2つの果物を変数に割り当て、その値が表示されます。
5.残っている果物: foreachループで現在の配列の内容を順番に表示します。
6.配列の長さ: 最後に、count()関数を使って配列の要素数が3であることを表示します。
リストのような柔軟なデータ構造に慣れると、「リンクリスト」「スタック」「キュー」など、さらに特殊なリストの概念にも触れることができます。
・リンクリスト(Linked List)
- 概要: 要素同士が「次の要素」へのリンク(ポインタ)を持っていて、ひとつながりのリストを形成しています。
- 特徴:
- メモリの連続領域を必要とせず、データの追加・削除が柔軟。
- 配列と違い、要素を挿入・削除する際に他の要素を移動させる必要がありません。
使いどころ: データの追加・削除が頻繁に発生する場面。たとえば、メモリ効率を優先したキューやスタック実装で使われることがあります。
・スタック(Stack)
- 概要: 「後入れ先出し(LIFO: Last In, First Out)」のルールに従うデータ構造です。
- 特徴:
- 最後に入れたデータが最初に取り出されるため、履歴や「戻る」操作に向いています。
- データを追加する「プッシュ」と、データを取り出す「ポップ」操作が基本です。
- 使いどころ: ブラウザの「戻る」ボタンやテキストエディタの「アンドゥ」機能など、処理を一時的に記憶し、順番に取り出したい場面。
・キュー(Queue)
- 概要: 「先入れ先出し(FIFO: First In, First Out)」のルールに従うデータ構造です。
- 特徴:
- 最初に追加されたデータが最初に取り出されるため、順番に処理が必要な場面に適しています。
- データを追加する「エンキュー」と、データを取り出す「デキュー」操作が基本です。
- 使いどころ: タスクの順番待ちや、サーバーへのリクエスト処理など、データを順番に処理する必要がある場面。
マップ(Map)
次に理解したのが「マップ(Map)」と呼ばれるデータ構造です。マップはキーと値のペアでデータを管理するもので、データを素早く検索するのに適しています。例えば、ユーザーIDとその設定情報を関連付けるといったケースで活躍するのがMapです。インフラ業務においても、サーバーの名前とIPアドレスをMapで紐づけて管理すれば、特定のサーバーを探す際に便利です。
下記にサーバーの名前とIPアドレスをマップ(連想配列)で紐づけて管理するPHPのサンプルコードを作成してみました。
このコードでは、サーバーの名前をキー、IPアドレスを値として管理します。
<?php
// サーバー名とIPアドレスの連想配列を作成
$servers = [
"web_server" => "192.168.1.10",
"db_server" => "192.168.1.20",
"cache_server" => "192.168.1.30",
"file_server" => "192.168.1.40"
];
// サーバー情報を表示
echo "初めのサーバー情報:\n";
print_r($servers);
// 新しいサーバーを追加
$servers["mail_server"] = "192.168.1.50";
echo "新しいサーバーを追加した後:\n";
print_r($servers);
// サーバーのIPアドレスを検索
$searchServer = "db_server";
if (array_key_exists($searchServer, $servers)) {
echo "$searchServer のIPアドレス: " . $servers[$searchServer] . "\n";
} else {
echo "$searchServer は存在しません。\n";
}
// サーバーを削除
unset($servers["cache_server"]); // cache_server を削除
echo "cache_server を削除した後:\n";
print_r($servers);
// 最終的なサーバー情報を表示
echo "最終的なサーバー情報:\n";
foreach ($servers as $serverName => $ipAddress) {
echo "サーバー名: $serverName, IPアドレス: $ipAddress\n";
}
?>
実行結果
初めのサーバー情報:
Array
(
[web_server] => 192.168.1.10
[db_server] => 192.168.1.20
[cache_server] => 192.168.1.30
[file_server] => 192.168.1.40
)
新しいサーバーを追加した後:
Array
(
[web_server] => 192.168.1.10
[db_server] => 192.168.1.20
[cache_server] => 192.168.1.30
[file_server] => 192.168.1.40
[mail_server] => 192.168.1.50
)
db_server のIPアドレス: 192.168.1.20
cache_server を削除した後:
Array
(
[web_server] => 192.168.1.10
[db_server] => 192.168.1.20
[file_server] => 192.168.1.40
[mail_server] => 192.168.1.50
)
最終的なサーバー情報:
サーバー名: web_server, IPアドレス: 192.168.1.10
サーバー名: db_server, IPアドレス: 192.168.1.20
サーバー名: file_server, IPアドレス: 192.168.1.40
サーバー名: mail_server, IPアドレス: 192.168.1.50
- サーバー情報の作成: サーバー名をキー、IPアドレスを値とする連想配列
$servers
を作成します。 - サーバー情報を表示: 初期のサーバー情報を表示します。
- 新しいサーバーを追加: 新しいサーバー(メールサーバー)を追加します。
- サーバーのIPアドレスを検索: 指定したサーバー名(
db_server
)が存在するか確認し、そのIPアドレスを表示します。 - サーバーを削除: 指定したサーバー名(
cache_server
)を削除します。 - 最終的なサーバー情報を表示: 現在のサーバー情報をループで表示します。
基礎から学び直した感想
ここまで基礎的なデータ構造を学び直してみて、インフラエンジニアとしての私の視点も大きく変わりました。インフラとプログラミングは一見すると異なる分野のように思えますが、データを整理し効率的に操作する点において共通する部分が多いことに気づきました。私もインフラをコードで管理するTerraformなどを実行する際に、自然とデータ構造の恩恵を受けていたようです。
今後はさらにオブジェクト指向やアルゴリズムにも触れ、インフラ作業の自動化やデータ管理の効率化に活かしていきたいと思います。
カテゴリー: