イテレータで竹藪を焼く

メニュー

今日は、Javaイテレータで竹藪を焼いてみます。

動機

Iterableの挙動を変えたい。
拡張forはシンタックスが分かりやすいので、もっと使われてもいいと思う。

元ネタ

うろ覚えだがScalaの教科書、Programming in Scala
http://www.amazon.co.jp/Programming-Scala-Comprehensive-Step-step/dp/0981531601
にListにmapする話があったと思う。

方針

Iterableのラッパークラスを作ってimplements Iterableにする。
まずただのラッパークラスをひとつ準備。Iterableと同じ型パラメータ(ここではT)をつけておく。

package iterator;

import java.util.Iterator;

public class IterableWrapper<T> {
	
	protected Iterable<T> iterable;
	protected Iterator<T> iterator;
	
	public IterableWrapper(Iterable<T> iterable) {
		setIterable(iterable);
	}

	public void setIterable(Iterable<T> iterable) {
		this.iterable = iterable;
		this.iterator = iterable.iterator();
	}
	
}

実装

map (各要素を変換) するIterable

要素AをBに変換して出力するIterableをつくる。
convertValueが変換に使うメソッド。

package iterator;

import java.util.Iterator;

public abstract class MapIterable<A,B> extends IterableWrapper<A> implements Iterable<B>{

	protected abstract B convertValue(A input);
	
	public MapIterable(Iterable<A> iterable) {
		super(iterable);
	}

	@Override
	public Iterator<B> iterator() {
		return new Iterator<B>(){

			@Override
			public boolean hasNext() {
				return iterator.hasNext();
			}

			@Override
			public B next() {
				//A -> Bの変換
				return convertValue(iterator.next());
			}

			@Override
			public void remove() {
				iterator.remove();
			}
			
		};
	}
	
}

数字を文字列に変換する例

package iterator;

import java.util.ArrayList;
import java.util.List;

public class Int2StringIterable extends MapIterable<Integer, String>{

	public static void main(String[] args) {
		List<Integer> nums = new ArrayList<Integer>();
		//数字を入れます
		for (int i = 1; i <= 5; i++) {
			nums.add(i);
		}

		Int2StringIterable i2s = new Int2StringIterable(nums);
		//文字列が出てきます
		for (String str : i2s) {
			System.out.println(str);
		}
	}
	
	public Int2StringIterable(Iterable<Integer> iterable) {
		super(iterable);
	}
	
	@Override
	protected String convertValue(Integer input) {
		if(input == 3){
			return input + "";
		} 
		if(input == 4){
			return input + "がなくて";
		}
		
		return input + "に努力";
	}

	
}

実行結果

1に努力
2に努力
3
4がなくて
5に努力
反転するIterable

一度中身のIterableをたどり、その後逆順にたどるようなIterableをつくる。
たけやぶを入れると焼ける。

package iterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;

import com.sun.xml.internal.ws.addressing.model.ActionNotSupportedException;

/**
 * Iterableをたどって逆順に戻ってくるIterableなラッパー。
 * @author aen
 * @param <T> Iterableの要素の型
 */
public class MirrorIterable<T> extends IterableWrapper<T> implements Iterable<T>{

	public static void main(String[] args) {
		
		List<Character> chars = new ArrayList<Character>();
		
		String takeyabu = "たけやぶ";
		char[] takeyabuc = takeyabu.toCharArray();
		//たけやぶをリストに詰めます
		for (char c : takeyabuc) {
			chars.add(c);
		}
		
		//ラッパーをかますと
		MirrorIterable<Character> mirrorNums = 
			new MirrorIterable<Character>(chars);
		//たけやぶが焼けます。
		for (char c : mirrorNums) {
			System.out.print(c);
		}		
		System.out.println();
		
		//2回かますと
		mirrorNums = new MirrorIterable<Character>(chars);
		MirrorIterable<Character> mirrorNums2 = 
			new MirrorIterable<Character>(mirrorNums);
		//もっと焼けます。
		for (char c : mirrorNums2) {
			System.out.print(c);
		}
		System.out.println();
	}
	
	
	/**
	 * たどったオブジェクトを積んでおくスタック。
	 */
	private Stack<T> stack;
	
	public MirrorIterable(Iterable<T> iterable) {
		super(iterable);
		stack = new Stack<T>();
	}

	@Override
	public void setIterable(Iterable<T> iterable) {
		super.setIterable(iterable);
		stack = new Stack<T>();
	}
	
	@Override
	public Iterator<T> iterator() {
		return new Iterator<T>(){

			@Override
			public boolean hasNext() {
				return (iterator.hasNext() || !stack.isEmpty());
			}

			@Override
			public T next() {
				//まだiteratorがたどれる
				if(iterator.hasNext()){
					T value = iterator.next();
					//最後はスタックに積まない
					if(iterator.hasNext()){
						//スタックに履歴を積んでおく
						stack.push(value);
					}
					return value;
				}
				//iteratorがたどれなくなったら、スタックから下ろしていく
				if(!stack.isEmpty()){
					return stack.pop();
				}
				
				throw new NoSuchElementException("no more element");
			}

			@Override
			public void remove() {
				throw new ActionNotSupportedException("remove not supported");
			}
			
		};
	}
	
}

実行結果

たけやぶやけた
たけやぶやけたけやぶやけた