みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

レイアウトファイルからレイアウトを作り出す

いつも参考にさせて頂いています。
現在レイアウトファイルを読み込み、そのレイアウトどおりにある文字列を整形する機能を作っています。実装方法がわからず、考えるに考えたのですがわかりませんでした。
どうかご教授よろしくお願いします。

--------------以下レイアウトファイル

(1)  (2)   (3)    (4)    (5)    (6)    (7)
0  5   G001    64    C    G    4
8  10   G002    8    C       0
9  10   G003    8    C       0
16  10   G004    8    C       0
24  10   G005    8    C       3
49  10   G006    8    C       0
57  10   G007    8    C       0
64  5   G010    8    C       0

--------------------------
上記がレイアウトファイルになります。
現状の実装としては(まだ途中ですが)
左から順番に
(1)オフセット
(2)レベル
(3)データタイプ(これをキーにArrayListから1行分のデータを取り出します)
(4)タイプ
(5)グループ
(6)繰り返し回数

という具合にタブ区切りのテキストがあり、1行ずつBufferedReaderで読み込み、splitで区切ったものをBeanクラスに入れていってます。
(最初に全部読み込み、ArrayListの中に突っ込んでます)
取り出す際には、(3)のデータ名をキーに1行分のデータを取得してます。そこで…

下記要件に沿ったものを作ろうとしています。
(1)要件
取り出した項目にGがあった場合、次行からレベルがあがり、元のレベルに戻るまでの間を1グループとします。上記の場合なら(2行目~6行目)になります。

もしグループに"G"と入っている項目を取り出そうとしたとき、(上記では"G001")2行目から6行目までをとりだすという機能なのですが、更に繰り返しというものがあります。繰り返しがある場合は、(たとえば"G005"をキーに値をとりだした場合)その回数分データをとりだします。上記の場合だと"G005"を3回ループしてから次の行に移ります。しかしGに繰り返しがあると、そのグループを繰り返さないといけません。上記の場合だと"G001"をキーに値をとりだす場合、"2行目~6行目を4回ループ"という処理をしなくてはいけない?と思ってます。

すいません、文章だとうまくいえず…

更にグループの中にまたグループがある場合もあります。

こういう処理はどう実装すればいいのでしょうか?
階層的?再帰処理?とか色々調べてはみたのですが…

うまく説明できたかどうかわかりませんがどうかよろしくお願いします。

投稿日時 - 2007-07-02 21:31:09

QNo.3134335

暇なときに回答ください

質問者が選んだベストアンサー

「Interpreterパターンの構文木」を作る方法も、
わかりやすいですね・・・。

class Element{void execute(){}}

class Group extends Element{
  int repeat;
  Vector children=new Vector();
  Group(){this(1);}
  Group(int p){repeat=p;}
  void add(Element e){children.add(e);}
 
  void execute(){
   for(int i=0;i<repeat;i++){//繰り返し 
    for(int j=0;j<children.size();j++){
     //子へ委譲
     ((Element)children.elementAt(j)).execute();
    } 
   }
  }
}

class Print extends Element{
 String message;
 Print(String p){message=p;}
 void execute(){System.out.print(message);}
}

public class Demo{
 Inst inst;
 int pos;
 
 /*スクリプト*/
 Inst[] script={
  new Inst("PRINT","Z")
   ,new Inst("GROUP",2)
    ,new Inst("PRINT","A")
    ,new Inst("GROUP",3)
     ,new Inst("PRINT","B")
     ,new Inst("PRINT","C")
    ,new Inst("GROUP_END")
   ,new Inst("GROUP_END")
  };
 
 void get(){
  if(pos==script.length){inst=null;return;}
  inst=script[pos++];
 }
  
 void inGroup(Group parent/*親ノード*/){
  while(true){
   get();
   if(null==inst){break;}
   
   String type=inst.type;    
   if("GROUP".equals(type)){
    Group g=new Group(inst.intOperand);
    parent.add(g);
    inGroup(g);//再帰
   }else if("PRINT".equals(type)){
     parent.add(new Print(inst.strOperand));
   }else if("GROUP_END".equals(type)){
    break;
   } 
  }
 }
 
 void parse(Group root){inGroup(root);}
 
 public static void main(String[] args){
  Group root =new Group();//ルートノード
  (new Demo()).parse(root);//パース
  root.execute();//実行
  System.out.println("");
 }
}

投稿日時 - 2007-07-09 19:40:39

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(2)

ANo.1

素人です。
専門知識を持ち合わせていませんが、悪しからずお願いします。
---

GROUP 3
PRINT "hello,world"
GROUP_END

というようなスクリプトを解釈するデモプログラムを書いてみました。
"(GROUPの)入れ子"をうまく処理するのに、Stackを利用。
ネット上にある「BASICインタプリタ」のFOR~NEXT文処理を参考にしました。

<思ったこと>
・「GROUP開始命令」と対になる、「GROUP終了命令」も作ったほうが、
パーサを書く際、考えやすいかも。
・もともとのスクリプト(のパーサを書くの)が考えにくければ、
"前処理"として、
いったん"自分の考えやすい中間的なスクリプト"に変換しておく
という手も。
(たとえば質問者さんが掲示された"レイアウトファイル"の
"G010の行"の前に一行、「GROUP終了に相当する命令」を加えるのは容易)
・どうしてもパーサが書けないなら、とりあえずxmlで。

---
import java.io.*;
import java.util.*;


class Data{
 int pos;
 int data1=1;
 int data2;
 Data(int p,int d){pos=p;data2=d;}
}

class Inst{
 String type;
 int intOperand;
 String strOperand;
 
 Inst(String t){type=t;}
 
 Inst(String t,int o){type=t;intOperand=o;}
 
 Inst(String t,String o){type=t;strOperand=o;}
}

public class Demo{
 Inst inst;
 int pos;//プログラムの位置
 Inst[] arr={
  new Inst("GROUP",3)
  ,new Inst("PRINT","hello, world")
  ,new Inst("GROUP_END")
  };
 Stack stack=new Stack();
 
 void get(){
  if(pos==arr.length){inst=null;return;}
  inst=arr[pos++];
 }
 
 void parse(){
  while(true){
   get();
   if(null==inst){break;}
   
   int repeat;
   String type=inst.type;    
   if("GROUP".equals(type)){
    repeat=inst.intOperand;
    stack.push(new Data(pos,repeat));
   } else if("GROUP_END".equals(type)){
    Data d=(Data)stack.peek();
    repeat=d.data2;
    if(d.data1==repeat){
     stack.pop();
    }else{
     d.data1++;
     pos=d.pos;
    }
   }else if("PRINT".equals(type)){
     System.out.println(inst.strOperand);
   } 
  }
 }
 
 public static void main(String[] args){
  (new Demo()).parse();
 }
}

投稿日時 - 2007-07-08 02:32:55

あなたにオススメの質問