02. リストデータの組み合わせ
以下のようなデータを扱うとしたらどうするか、という話を書こうと思います。
- テレビのメーカー一覧、を指定すると、メーカー名のリストが得られる
- メーカー名、を指定すると、そのメーカーのテレビ名のリストが得られる
- メーカー名とテレビ名、を指定すると、そのテレビの説明テキストが得られる
そんな管理には、ハッシュ(連想配列)が使えると便利です。そんな例を示したいと思います。
例で扱うデータ構造:
- テレビメーカーリストを定義(a,b,c)
- a,b,cそれぞれに型番リストと、その説明(型番商品の説明)を定義する
各スクリプトでは、以下のような実装方法を用います。
- tcsh
- リスト変数のみ使用
- Perl
- 配列とハッシュを使用
- Ruby, Python, C#
- 配列とハッシュとクラスを使用
--- tcsh
tcshで使えるのは、リスト変数のみです。種類ごとに異なる変数名をつけることで、複数種類のデータリストを扱うことができますが、あまり複雑なデータを扱うのは得意ではありません。
「メーカー名とテレビ名を指定」してテレビの説明を取り出す部分は、条件記述が複雑化してしまうため、説明用の配列にただ並べたものを表示させるだけにしています。
次の手順により、実装してみたいと思います。
- 変数定義
- 実行
- 各メーカーの情報を格納した変数を、条件分岐で共通変数へ格納し、以降の処理で共通変数を使用する
#!/usr/bin/tcsh -f
### Input Data ######################
# メーカー名リスト
set mTvMakerList = ( "TOSHIBA" "SONY" "PANASONIC" )
# メーカーごとの型番リスト
set mTsbTvList = ( "T1" "T2" "T3" )
set mSnyTvList = ( "S1" "S2" "S3" )
set mPnaTvList = ( "P1" "P2" "P3" )
# 各型番の説明
set mT1Desc = "Toshiba T1 TV desu"
set mT2Desc = "Toshiba T2 TV desu"
set mT3Desc = "Toshiba T3 TV desu"
set mS1Desc = "Sony S1 TV desu"
set mS2Desc = "Sony S2 TV desu"
set mS3Desc = "Sony S3 TV desu"
set mP1Desc = "Panasonic P1 TV desu"
set mP2Desc = "Panasonic P2 TV desu"
set mP3Desc = "Panasonic P3 TV desu"
### run ##########################
foreach maker ( $mTvMakerList )
echo "--- $maker"
if ( $maker == "TOSHIBA" ) then
set mTvList = ( $mTsbTvList )
set mDesc = ( "$mT1Desc" "$mT2Desc" "$mT3Desc" )
else if ( $maker == "SONY" ) then
set mTvList = ( $mSnyTvList )
set mDesc = ( "$mS1Desc" "$mS2Desc" "$mS3Desc" )
else if ( $maker == "PANASONIC" ) then
set mTvList = ( $mPnaTvList )
set mDesc = ( "$mP1Desc" "$mP2Desc" "$mP3Desc" )
endif
@ index = 1
foreach name ( $mTvList )
echo " +-$name"
echo " | +-$mDesc[$index]"
@ index ++
end
end
---------------------------------
実行結果:
--- TOSHIBA
+-T1
| +-Toshiba T1 TV desu
+-T2
| +-Toshiba T2 TV desu
+-T3
| +-Toshiba T3 TV desu
--- SONY
+-S1
| +-Sony S1 TV desu
+-S2
| +-Sony S2 TV desu
+-S3
| +-Sony S3 TV desu
--- PANASONIC
+-P1
| +-Panasonic P1 TV desu
+-P2
| +-Panasonic P2 TV desu
+-P3
| +-Panasonic P3 TV desu
--- Perl
プログラムは3つのブロックに分かれています。
- 入力ブロック
- データベース生成ブロック(ハッシュ生成)
- データベース読み出しブロック
ハッシュ %mTvDB を定義しますが、ハッシュに配列を格納するためには、配列のリファレンス(\@array、バックスラッシュをつける)を代入する必要があり、また取り出すときはデリファレンス(@{$mTvDB{key}})の形を使う必要があります。変数値(xxDesc)の格納は、リファレンスにする必要はありません。
ハッシュへのデータ登録時、キーをカンマ区切りで行っているところがありますが、これは実際には、ただ2つの文字列の間にカンマ文字列が入るに過ぎません。
#!/usr/bin/perl
use warnings;
### Input Data ###########################
# メーカー名の配列
@mTvMakerList = ("TOSHIBA","SONY","PANASONIC");
# 各メーカーの機種名配列
@mTsbTvList = ("T1","T2","T3");
@mSnyTvList = ("S1","S2","S3");
@mPnaTvList = ("P1","P2","P3");
# 各機種の説明
$mT1Desc = "Toshiba T1 TV desu";
$mT2Desc = "Toshiba T2 TV desu";
$mT3Desc = "Toshiba T3 TV desu";
$mS1Desc = "Sony S1 TV desu";
$mS2Desc = "Sony S2 TV desu";
$mS3Desc = "Sony S3 TV desu";
$mP1Desc = "Panasonic P1 TV desu";
$mP2Desc = "Panasonic P2 TV desu";
$mP3Desc = "Panasonic P3 TV desu";
### Create DB #############################
# ハッシュ定義。makerというキーに、メーカー名配列のリファレンスを格納
$mTvDB{maker} = \@mTvMakerList;
# 各メーカー名のキーに、機種名配列のリファレンスを格納
$mTvDB{$mTvMakerList[0]} = \@mTsbTvList;
$mTvDB{$mTvMakerList[1]} = \@mSnyTvList;
$mTvDB{$mTvMakerList[2]} = \@mPnaTvList;
# メーカー名+機種名のキーに、説明値を格納
$mTvDB{$mTvMakerList[0],$mTsbTvList[0]} = $mT1Desc;
$mTvDB{$mTvMakerList[0],$mTsbTvList[1]} = $mT2Desc;
$mTvDB{$mTvMakerList[0],$mTsbTvList[2]} = $mT3Desc;
$mTvDB{$mTvMakerList[1],$mSnyTvList[0]} = $mS1Desc;
$mTvDB{$mTvMakerList[1],$mSnyTvList[1]} = $mS2Desc;
$mTvDB{$mTvMakerList[1],$mSnyTvList[2]} = $mS3Desc;
$mTvDB{$mTvMakerList[2],$mPnaTvList[0]} = $mP1Desc;
$mTvDB{$mTvMakerList[2],$mPnaTvList[1]} = $mP2Desc;
$mTvDB{$mTvMakerList[2],$mPnaTvList[2]} = $mP3Desc;
### run
foreach $name0( @{$mTvDB{maker}} ){
print "--- $name0\n";
foreach $name1( @{$mTvDB{$name0}} ){
print " +-$name1\n";
print " | +-$mTvDB{$name0,$name1}\n";
}
}
実行結果はtcshと同じなので省略します。
--- Ruby
rubyではclassを使って実装してみます。技術的なポイントとしては、ハッシュに配列やclassを格納しているが、Perlのようにポインタ渡し(リファレンス)する必要がないところです。
#!/usr/bin/ruby -w
### class
class TvInfo
def initialize(name,desc)
@name = name
@desc = desc
end
attr_accessor :name, :desc
end
### Input Data
# メーカー名の配列を定義
mTvMakerList = %W[TOSHIBA SONY PANASONIC]
# クラスを生成し、機種名と説明を格納する
t1TvInfo = TvInfo.new("T1", "Toshiba T1 TV desu")
t2TvInfo = TvInfo.new("T2", "Toshiba T2 TV desu")
t3TvInfo = TvInfo.new("T3", "Toshiba T3 TV desu")
s1TvInfo = TvInfo.new("S1", "Sony S1 TV desu")
s2TvInfo = TvInfo.new("S2", "Sony S2 TV desu")
s3TvInfo = TvInfo.new("S3", "Sony S3 TV desu")
p1TvInfo = TvInfo.new("P1", "Panasonic P1 TV desu")
p2TvInfo = TvInfo.new("P2", "Panasonic P2 TV desu")
p3TvInfo = TvInfo.new("P3", "Panasonic P3 TV desu")
### Create DB
mTvDB = Hash.new #initialize
# ハッシュに配列を登録
mTvDB.store("maker", mTvMakerList)
# classの配列を定義
mTsbTvList = [t1TvInfo, t2TvInfo, t3TvInfo]
mSnyTvList = [s1TvInfo, s2TvInfo, s3TvInfo]
mPnaTvList = [p1TvInfo, p2TvInfo, p3TvInfo]
# ハッシュに配列を登録
mTvDB.store(mTvMakerList[0], mTsbTvList)
mTvDB.store(mTvMakerList[1], mSnyTvList)
mTvDB.store(mTvMakerList[2], mPnaTvList)
### run
mTvMakerList.each { |name0|
puts "--- #{name0}"
mTvDB.fetch("#{name0}").each { |item|
puts " +-" + item.name
puts " | +-" + item.desc
}
}
実行結果はtcshと同じなので省略します。
--- Python
処理構造はRubyと同等です。
#!/usr/bin/python
### class
class TvInfo:
def __init__(self, name, desc):
self.name = name
self.desc = desc
### Input Data
# メーカー名の配列を定義
mTvMakerList = ['TOSHIBA', 'SONY', 'PANASONIC']
# クラスを生成し、機種名と説明を格納する
t1TvInfo = TvInfo("T1", "Toshiba T1 TV desu")
t2TvInfo = TvInfo("T2", "Toshiba T2 TV desu")
t3TvInfo = TvInfo("T3", "Toshiba T3 TV desu")
s1TvInfo = TvInfo("S1", "Sony S1 TV desu")
s2TvInfo = TvInfo("S2", "Sony S2 TV desu")
s3TvInfo = TvInfo("S3", "Sony S3 TV desu")
p1TvInfo = TvInfo("P1", "Panasonic P1 TV desu")
p2TvInfo = TvInfo("P2", "Panasonic P2 TV desu")
p3TvInfo = TvInfo("P3", "Panasonic P3 TV desu")
### Create DB
mTvDB = {} #hash init
# ハッシュに配列を格納
mTvDB['maker'] = mTvMakerList
# classの配列を定義
mTsbTvList = [t1TvInfo, t2TvInfo, t3TvInfo]
mSnyTvList = [s1TvInfo, s2TvInfo, s3TvInfo]
mPnaTvList = [p1TvInfo, p2TvInfo, p3TvInfo]
# ハッシュに配列を格納
mTvDB[mTvMakerList[0]] = mTsbTvList
mTvDB[mTvMakerList[1]] = mSnyTvList
mTvDB[mTvMakerList[2]] = mPnaTvList
for maker in mTvDB['maker']:
print "--- %s" % (maker)
for item in mTvDB[maker]:
print " +-%s" % (item.name)
print " | +-%s" % (item.desc)
実行結果はtcshと同じなので省略します。
---C#
C#でも、Ruby, Pythonと同様に自作クラスを使用しています。
下記コード中、①を他の言語同様に記述していますが、使用していません。RubyやPythonでは、foreachを記述する際に変数展開元のHashに"maker"を指定して配列を取得し、その配列をforeachで回しているのですが、C#ではそれができず(コンパイラに怒られました)、仕方がないので一度変数に取り出してからforeachを回すという処理をしています。C#は実行速度が高速ですが、こういうケースではちょっと面倒でした。
using System;
using System.Collections;
class TvInfo {
string name, desc;
public TvInfo(string name, string desc){
this.name = name;
this.desc = desc;
}
public string getName(){
return this.name;
}
public string getDesc(){
return this.desc;
}
}
class Test {
public static void Main(string[] args){
// メーカー名の配列を定義
string[] mTvMakerList = new string[] {"TOSHIBA", "SONY", "PANASONIC"};
// クラスを生成し、機種名を説明を格納する
TvInfo t1TvInfo = new TvInfo("T1", "Toshiba T1 TV desu");
TvInfo t2TvInfo = new TvInfo("T2", "Toshiba T2 TV desu");
TvInfo t3TvInfo = new TvInfo("T3", "Toshiba T3 TV desu");
TvInfo s1TvInfo = new TvInfo("S1", "Sony S1 TV desu");
TvInfo s2TvInfo = new TvInfo("S2", "Sony S2 TV desu");
TvInfo s3TvInfo = new TvInfo("S3", "Sony S3 TV desu");
TvInfo p1TvInfo = new TvInfo("P1", "Panasonic P1 TV desu");
TvInfo p2TvInfo = new TvInfo("P2", "Panasonic P2 TV desu");
TvInfo p3TvInfo = new TvInfo("P3", "Panasonic P3 TV desu");
// Create DB
Hashtable mTvDB = new Hashtable();
mTvDB["maker"] = mTvMakerList; // ①
TvInfo[] mTsbTvList = new TvInfo[] {t1TvInfo, t2TvInfo, t3TvInfo};
TvInfo[] mSnyTvList = new TvInfo[] {s1TvInfo, s2TvInfo, s3TvInfo};
TvInfo[] mPnaTvList = new TvInfo[] {p1TvInfo, p2TvInfo, p3TvInfo};
mTvDB[mTvMakerList[0]] = mTsbTvList;
mTvDB[mTvMakerList[1]] = mSnyTvList;
mTvDB[mTvMakerList[2]] = mPnaTvList;
foreach(string maker in mTvMakerList){
Console.WriteLine("--- {0}", maker);
TvInfo[] infoList = (TvInfo[]) mTvDB[maker]; // ②
foreach(TvInfo info in infoList){ // ②
Console.WriteLine(" +-{0}", info.getName());
Console.WriteLine(" | +-{0}", info.getDesc());
}
}
}
}
実行結果は省略します。