argius note

プログラミング関連

JavaでLL言語のようにリスト(配列)を操作したい

Lisp発祥かと思いますが、LL言語でリストor配列を扱う関数orメソッドのようなことを、Javaでやりたい場合はどうやるのが一般的なのでしょうか。
具体的にはRubyで例を挙げてみますが、

ia = [3, 2, 7]
p( ia.select() do |item| item >= 3 end )            # => [3, 7]
p( ia.inject() do |result, item| result + item end )# => 12

sa = %w[tw el ve]
p( sa.grep(/[tv]/) )                                # => ["tw", "ve"]
p( sa.select() do |item| item.include?("e") end )   # => ["el", "ve"]
p( sa.inject() do |result, item| result + item end )# => "twelve"

oa = [12.0, Time.new, "13", 14]
p( oa.select() do |item| item.is_a?(Numeric) end )  # => [12.0, 14]

のような処理です。

ここでは、RubyのEnumerableのインタフェースを参考に、それっぽいのをJava(5.0対応)で作ってみました。まだ途中ですが、ちょっと長めです。


まずは本体。

import java.util.*;
import java.util.regex.*;

abstract class Enumerable<E> {

    interface Selectable<E> {
        boolean select(E e);
    }

    interface Injectable<E> {
        E inject(E result, E e);
    }

    public Array<E> grep(Pattern pattern) {
        return grep(pattern, new Selectable<E>() {
            public boolean select(E e) {
                return true;
            }
        });
    }

    public Array<E> grep(final Pattern pattern, final Selectable<E> selectable) {
        return each(new Selectable<E>() {
            public boolean select(E e) {
                if (e != null) {
                    return pattern.matcher(e.toString()).find() && selectable.select(e);
                }
                return false;
            }
        });
    }

    public Array<E> select(Selectable<E> selectable) {
        return each(selectable);
    }

    public E inject(Injectable<E> injectable) {
        return inject(injectable, null);
    }

    public E inject(Injectable<E> injectable, E initialValue) {
        return each(injectable, initialValue);
    }

//    public abstract Array<E> sort();
//    public abstract Array<E> sort(Comparable<E> comparable);

    /* each */

    public abstract Array<E> each(Selectable<E> selectable);
    public abstract E each(Injectable<E> injectable, E initialValue);

}

class Array<E> extends Enumerable<E> {

    private List<E> list;

    public Array() {
        this.list = new ArrayList<E>();
    }

    public Array(E[] objects) {
        this(Arrays.asList(objects));
    }

    public Array(Collection<E> collection) {
        this.list = new ArrayList<E>(collection);
    }

    public static <E> Array<E> valueOf(E... objects) {
        return new Array<E>(objects);
    }

    public boolean add(E e) {
        return list.add(e);
    }

    public E get(int index) {
        return list.get(index);
    }

    public int size() {
        return list.size();
    }

    @Override
    public String toString() {
        return list.toString();
    }

    @Override
    public Array<E> each(Selectable<E> selectable) {
        Array<E> array = new Array<E>();
        for (E e : list) {
            if (selectable.select(e)) {
                array.add(e);
            }
        }
        return array;
    }

    @Override
    public E each(Injectable<E> injectable, E initialValue) {
        E result = initialValue;
        boolean isFirst = true;
        for (E e : list) {
            if (isFirst) {
                if (result == null) {
                    result = e;
                    continue;
                }
                isFirst = false;
            }
            result = injectable.inject(result, e);
        }
        return result;
    }

}

テストコード。

Array<Integer> ia = Array.valueOf(3, 2, 7);
System.out.println(ia.select(new Enumerable.Selectable<Integer>() {
    public boolean select(Integer item) {
        return item >= 3;
    }
}));
System.out.println(ia.inject(new Enumerable.Injectable<Integer>() {
    public Integer inject(Integer result, Integer item) {
        return result + item;
    }
}));

Array<String> sa = Array.valueOf("tw", "el", "ve");
System.out.println(sa.grep(Pattern.compile("[tv]")));
System.out.println(sa.select(new Enumerable.Selectable<String>() {
    public boolean select(String item) {
        return item.indexOf('e') >= 0;
    }
}));
System.out.println(sa.inject(new Enumerable.Injectable<String>() {
    public String inject(String result, String item) {
        return result + item;
    }
}));

Array<Object> oa = Array.valueOf(12.0, (Object)new Date(), "13", 14);
System.out.println(oa.select(new Enumerable.Selectable<Object>() {
    public boolean select(Object item) {
        return (item instanceof Number);
    }
}));

// おまけ
System.out.println(Array.valueOf("A", "B").grep(Pattern.compile("A")));

全体的に見づらいですが、意味重視ということで。他のメソッドも同じ要領で作れそうです。ファクトリはもうちょっときれいにしたいところですが、時間切れのため次回に。

実行結果です。

[3, 7]
12
[tw, ve]
[el, ve]
twelve
[12.0, 14]
[A]