以下のようなデータを扱うとしたらどうするか、という話を書こうと思います。
そんな管理には、ハッシュ(連想配列)が使えると便利です。そんな例を示したいと思います。
例で扱うデータ構造:
各スクリプトでは、以下のような実装方法を用います。
--- 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 ++ endend---------------------------------
実行結果:
--- 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/perluse 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;### runforeach $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### classclass TvInfo def initialize(name,desc) @name = name @desc = desc end attr_accessor :name, :descend### 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 DBmTvDB = 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)### runmTvMakerList.each { |name0| puts "--- #{name0}" mTvDB.fetch("#{name0}").each { |item| puts " +-" + item.name puts " | +-" + item.desc }}実行結果はtcshと同じなので省略します。
--- Python
処理構造はRubyと同等です。
#!/usr/bin/python### classclass 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 DBmTvDB = {} #hash init# ハッシュに配列を格納mTvDB['maker'] = mTvMakerList# classの配列を定義mTsbTvList = [t1TvInfo, t2TvInfo, t3TvInfo]mSnyTvList = [s1TvInfo, s2TvInfo, s3TvInfo]mPnaTvList = [p1TvInfo, p2TvInfo, p3TvInfo]# ハッシュに配列を格納mTvDB[mTvMakerList[0]] = mTsbTvListmTvDB[mTvMakerList[1]] = mSnyTvListmTvDB[mTvMakerList[2]] = mPnaTvListfor 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()); } } }}実行結果は省略します。