
public class Node {
	private Object head;
	private Node rest;
	public Node(Object head, Node rest) {
		assert(head != null);
		this.head = head;
		this.rest = rest;
	}
	
	public Object getHead() {
		return this.head;
	}
	
	public Node getRest() {
		return this.rest;
	}
	
	public String toShortBodyString() {
		if(head == null)
			return null;
		return String.format(" %s%s", head.toString(), (rest != null) ? rest.toShortBodyString() : "");
	}
	
	@Override
	public String toString() {
		String result = toShortBodyString();
		if(result != null) {
			assert(result.charAt(0) == ' ');
			return String.format("(%s)", result.substring(1));
		} else {
			assert(head != null);
			return String.format("(cons %s%s)", head.toString(), (rest != null) ? " " + rest.toString() : " nil");
		}
	}
	
	public static Node map(NodeCallback callback, Node node) {
		return new Node(callback.callback(node.getHead()), map(callback, node.getRest()));
	}

	public static Object accumulate(NodeCallback callback, Object result, Node node) {
		if(node == null)
			return result;
		else
			return accumulate(callback, callback.callback2(result, node.getHead()), node.getRest());  
	}

	/* (map (zip */
	public static Node zip2map(NodeCallback callback, Node node1, Node node2) {
		return new Node(callback.callback2(node1.getHead(), node2.getHead()), zip2map(callback, node1.getRest(), node2.getRest()));
	}

	public static Object reverseAccumulate(NodeCallback nodeCallback, Object result, Node node) {
		Node reversedList = (Node) accumulate(new NodeCallback() {
			@Override
			public Object callback2(Object result, Object item) {
				return new Node(item, (result != null) ? (Node) result : null); 
			}
		}, null, node);
		return accumulate(nodeCallback, result, reversedList);
	}

}
