Igoという形態素解析器をつかって圧縮新聞っぽいものを作ってみる

うどんのこんぶだしが取れるまで時間があったので、ちょっと試しにつくってみました。
簡単なプログラムだけど、それっぽい結果がでてます。登録する文章しだいで結構おもしろくなりそう。


Igoはこれです。
Igo - a morphological analyzer
ここに書いてある手順で辞書バイナリを作ってください。
あとは、ソース中でその位置を指定します。


で、上側のテキストエリアに新聞記事をはりつけて「登録」ボタンを押します。


いくつか新聞記事を登録して「生成」ボタンを押すと、なんかそれっぽい文章が生成されます。

文章の生成自体は、簡単なプログラムでそれっぽいものができることがわかりました。
まあ、文章のクロールと、あとは記号なんかの処理が大事そうです。違う分野の文章をまぜたほうがおもしろい文章が生成されそう。


追記--
うどんおいしくできあがりました。

--追記ここまで


めんどいので、NetBeansの生成したそのままのソース。Java7でコンパイルするか、<>オペレータを適当に修正してください。

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import net.reduls.igo.Morpheme;
import net.reduls.igo.Tagger;

/**
 * @author naoki
 */
public class AsshukuFrame extends javax.swing.JFrame {
    Tagger tagger;
    /** Creates new form AsshukuFrame */
    public AsshukuFrame() {
        try {
            tagger = new Tagger("path to dic");//辞書バイナリへのパス
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        noSeparate.addAll(Arrays.asList("「", "」", "(", ")","、", "・"));
        initComponents();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        taArticle = new javax.swing.JTextArea();
        jScrollPane2 = new javax.swing.JScrollPane();
        taLog = new javax.swing.JTextArea();
        btnRegister = new javax.swing.JButton();
        btnGenerate = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        taArticle.setColumns(20);
        taArticle.setRows(5);
        jScrollPane1.setViewportView(taArticle);

        taLog.setColumns(20);
        taLog.setRows(5);
        jScrollPane2.setViewportView(taLog);

        btnRegister.setText("登録");
        btnRegister.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnRegisterActionPerformed(evt);
            }
        });

        btnGenerate.setText("生成");
        btnGenerate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnGenerateActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 470, Short.MAX_VALUE)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 470, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(btnGenerate)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btnRegister)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 112, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(btnRegister)
                    .addComponent(btnGenerate))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 270, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>

    static Set<String> noSeparate = new HashSet<>();
    
    private static class Morph{
        String word;
        String element;
        int nextCount = 0;

        public Morph() {
        }
        
        public Morph(Morpheme m) {
            word = m.surface;
            element = m.feature;
        }
        
        Map<Morph, Integer> counter = new HashMap<>();

        void add(Morph m){
            int count = 0;
            if(counter.containsKey(m)){
                count = counter.get(m);
            }
            counter.put(m, count + 1);
            ++nextCount;
        }
        boolean isSeparator(){
            if(!element.startsWith("記号,")){
                return false;
            }
            return !noSeparate.contains(word);
        }
        @Override
        public int hashCode() {
            return getKey().hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if(obj == null) return false;
            if(!(obj instanceof Morph)) return false;//継承したオブジェクトも受け入れてみる。
            
            return ((Morph)obj).getKey().equals(getKey());
        }
        
        public String getKey(){
            return element;
        }
    }
    private static class StartMorphs extends Morph{
    }
    private static class Endmorph extends Morph{
        @Override
        boolean isSeparator() {
            return true;
        }
    }
    
    Morph start = new StartMorphs();
    Morph end = new Endmorph();

    Map<Morph, Morph> morphs = new HashMap<>();//比較キーを試しに切り替えるためにこんなことに。
    
    private Morph createMorph(Morpheme m){
        Morph key = new Morph(m);
        if(morphs.containsKey(key)){
            return morphs.get(key);
        }else{
            morphs.put(key, key);
            return key;
        }
    }
    
    private void btnRegisterActionPerformed(java.awt.event.ActionEvent evt) {
        String str = taArticle.getText();
        str = str.trim();
        if(str.isEmpty()) return;
        
        //形態素解析
        List<Morpheme> morphList = tagger.parse(str);
        Morph preMorph = start;
        for(Morpheme m : morphList){
            Morph mm = createMorph(m);
            preMorph.add(mm);
            if(mm.isSeparator()){
                preMorph = start;
            }else{
                preMorph = mm;
            }
        }
        if(preMorph != start){
            preMorph.add(end);
        }
        taLog.append(morphList.size() + "単語を登録\n");
    }

    Random rand = new Random();
    private void btnGenerateActionPerformed(java.awt.event.ActionEvent evt) {
        Morph pre = start;
        WORD:
        for(int i = 0; i < 100; ++i){
            int next = rand.nextInt(pre.nextCount);
            int c = 0;
            for(Map.Entry<Morph, Integer> ent : pre.counter.entrySet()){
                c += ent.getValue();
                if(next < c){
                    pre = ent.getKey();
                    if(pre instanceof Endmorph) break WORD;
                    taLog.append(pre.word);
                    if(pre.isSeparator()) break WORD;
                    break;
                }
            }
        }
        taLog.append("\n");
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new AsshukuFrame().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JButton btnGenerate;
    private javax.swing.JButton btnRegister;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JTextArea taArticle;
    private javax.swing.JTextArea taLog;
    // End of variables declaration
}