sironekotoroの日記

Perl で楽をしたい

俺のリファレンス

言いたいこと

  • Perlのリファレンスは、以下のような「データをまとめたい!」って時に使うと楽になるんだよ
    • 配列の中に配列を「配列のまま」入れたい
    • 配列の中にハッシュを「ハッシュのまま」入れたい
    • ハッシュの中に配列を「配列のまま」入れたい
    • ハッシュの中にハッシュを「ハッシュのまま」入れたい
  • データをまとめると、どうお得なの?
    • 記述が楽
    • データ同士での比較が楽
  • 本当なの?という疑り深い人、2013年頃の私みたいな人は自分で配列に配列入れたり、配列にハッシュ入れたりして、自分の考えた通りのデータ構造が出来るか、そのデータ構造から要素を取り出せるか、やってみよう!
  • ・・・ってことを、昨日のPerl入学式のサポーターしながら考えたのでした。

配列に配列を入れる小芝居

use strict;
use warnings;
use Data::Dumper;

# 課題
# 「テレビ局の略称」と「チャンネル番号」を組み合わせた
# データ構造を作りたい
# あわよくば一覧表示を楽に実現したい

# まず、テレビ局のチャンネル番号と略称名の配列を作る
# 配列の最初の要素はチャンネル番号、次の要素はテレビ局名の略称と決める
# @配列 = (チャンネル番号 , 略称);
my @nhk = ( 1, 'NHK' );    # NHK総合
my @etv = ( 2, 'ETV' );    # eテレ(教育テレビ)
my @ntv = ( 4, 'NTV' );    # 日本テレビ
my @ex  = ( 5, 'EX' );     # テレビ朝日
my @tbs = ( 6, 'TBS' );    # TBS
my @tx  = ( 7, 'TX' );     # テレビ東京
my @cx  = ( 8, 'CX' );     # フジテレビ
my @mx  = ( 9, 'MX' );     # TOKYO MX

# チャンネル番号を全部表示したい・・・もしTV局が沢山あったら?
print '@nhkのチャンネル番号は:', $nhk[0], "\n";    # 1
print '@etvのチャンネル番号は:', $etv[0], "\n";    # 2
print '@ntvのチャンネル番号は:', $ntv[0], "\n";    # 4

# TV局の数だけ書いていく?
# 地方局も増えたらどうする?
# 配列名を人間が全部覚える?
# そんなの面倒!
# データをまとめておいて、forループ等でまとめて取りたい!

# チャンネルをまとめる配列を作る
my @channels;

# ----------------------
# 失敗編
# ----------------------

# チャンネルをまとめる配列に、テレビ局の配列を「そのまま」入れる
push @channels, @nhk;
push @channels, @etv;
push @channels, @ntv;

# 3つ入れてみたところで、試しにData::Dumperで中身を表示してみる
print Dumper @channels;

# $VAR1 = 1;
# $VAR2 = 'NHK';
# $VAR3 = 2;
# $VAR4 = 'ETV';
# $VAR5 = 4;
# $VAR6 = 'NTV';

# 元のTV局ごとの配列が崩れて、一つの配列になってる!?

print "@channels", "\n";    # 1 NHK 2 ETV 4 NTV
print $channels[0], "\n";   # 1
print $channels[1], "\n";   # NHK
print $channels[2], "\n";   # 2
print $channels[3], "\n";   # ETV

# それぞれの要素が何を示しているか、人間が覚えておかなくてはならない
# ・偶数の添字はチャンネル番号
# ・偶数の添字 + 1 はテレビ局名
# など。
# これは面倒では?
# チャンネル番号とテレビ局名を配列にした意味がないのでは?

# ----------------------
# 解決編
# ----------------------

# 配列をリファレンス化する
my $ref_nhk = \@nhk;
my $ref_etv = \@etv;
my $ref_ntv = \@ntv;
my $ref_ex  = \@ex;
my $ref_tbs = \@tbs;
my $ref_tx  = \@tx;
my $ref_cx  = \@cx;
my $ref_mx  = \@mx;

# ここで注目!
# さっきは「配列 @channnels」の中に「配列 @テレビ局」を入れたところ、
# 「配列 @channnels」の中で展開されてしまった。
# しかし!
# リファレンス化することで、配列を「スカラー」として扱うことが出来る!

# 「失敗編」で使った配列@channelsを初期化する
@channels = ();

# 配列@channelsにリファレンス化したテレビ局のデータを追加する
push @channels, $ref_nhk;
push @channels, $ref_etv;
push @channels, $ref_ntv;

# 3つ入れてみたところで、試しにData::Dumperで中身を表示してみる
print Dumper @channels;

# $VAR1 = [
#           1,
#           'NHK'
#         ];
# $VAR2 = [
#           2,
#           'ETV'
#         ];
# $VAR3 = [
#           4,
#           'NTV'
#         ];

# 俺が求めていたものはこれだ!
# 残りも追加していく
push @channels, $ref_ex;
push @channels, $ref_tbs;
push @channels, $ref_tx;
push @channels, $ref_cx;
push @channels, $ref_mx;

# どうなっているかな〜?
print Dumper @channels;

# $VAR1 = [
#           1,
#           'NHK'
#         ];
# $VAR2 = [
#           2,
#           'ETV'
#         ];
# $VAR3 = [
#           4,
#           'NTV'
#         ];
# $VAR1 = [
#           1,
#           'NHK'
#         ];
# $VAR2 = [
#           2,
#           'ETV'
#         ];
# $VAR3 = [
#           4,
#           'NTV'
#         ];
# $VAR4 = [
#           5,
#           'EX'
#         ];
# $VAR5 = [
#           6,
#           'TBS'
#         ];
# $VAR6 = [
#           7,
#           'TX'
#         ];
# $VAR7 = [
#           8,
#           'CX'
#         ];
# $VAR8 = [
#           9,
#           'MX'
#         ];

# ちゃんと全部、チャンネル番号とテレビ局名の組み合わせが入ってる!

# さて、データを取り出してみる
# 配列@channelsは配列なので、データの順番が保証されている
# 最初に入れたNHKの配列を取り出してみる

print $channels[0], "\n";
# ARRAY(0x************) 配列リファレンスであることを示す表示

# 配列リファレンスから、元の配列に戻す「デリファレンス」を行う
print @{ $channels[0] }, "\n";    # 1NHK
# 配列をそのままprintしたので要素が繋がって表示されている

# 中身は取得できたようなので、配列の要素ごとに取り出してみる
# 配列の要素はスカラーなので、$でアクセスする
print ${ $channels[0] }[0], "\n";    # 1
print ${ $channels[0] }[1], "\n";    # NHK

# アロー記法を使うとスマート表記が可能
print $channels[3]->[0], "\n";       # 5
print $channels[3]->[1], "\n";       # EX


# データ構造を構築できたところで、一気に表示してみる

# @channelsの中身は、解決編冒頭で作成した、配列リファレンスの集合で
# あることを忘れない
foreach my $array_ref ( @channels ) {
    # 配列リファレンスの最初の要素はチャンネル番号
    print "チャンネル番号は:" , $array_ref->[0], "\n";
    # 配列リファレンスの二番目の要素はテレビ局の略称
    print "テレビ局の略称は:" , $array_ref->[1], "\n";
};

# チャンネル番号は:1
# テレビ局の略称は:NHK
# チャンネル番号は:2
# テレビ局の略称は:ETV
# チャンネル番号は:4
# テレビ局の略称は:NTV
# チャンネル番号は:5
# テレビ局の略称は:EX
# チャンネル番号は:6
# テレビ局の略称は:TBS
# チャンネル番号は:7
# テレビ局の略称は:TX
# チャンネル番号は:8
# テレビ局の略称は:CX
# チャンネル番号は:9
# テレビ局の略称は:MX


# もし、最初に作ったテレビ局の配列から上記のような出力を得ようとした場合には・・・
print "チャンネル番号は:" , $nhk[0] , "\n";
print "テレビ局の略称は:" , $nhk[1] , "\n";
print "チャンネル番号は:" , $etv[0] , "\n";
print "テレビ局の略称は:" , $etv[1] , "\n";
# ・・・と、テレビ局の配列の数 * 2 記述する必要があり大変。
# 配列名を間違えたり、抜けがないように人間が気をつける必要がある。
# これは「楽」ではない
# でもまぁ、書けなくはない。大変だけど。