论坛首页 综合技术论坛

Java 函数式编程实验(新添Keyword Message)

浏览 7683 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-12-02  
FP
实验了以下内容:高阶函数,Currying,Lazy Evaluation,无穷流,Monad。都是很基本的东西。实现也是基于内部类的。没啥是了不起的。只是在给Lazy Evaluation造语法糖的时候,用了一下bytecode动态增强。给Lazy函数的lambda定义内部的所有的局部变量的读取操作前加了Lazy Evaluation过程。

private final static F<Boolean> TRUE = $(true);
private final static F<Integer> ONE = $(1);
private final static F<Integer> TWO = $(2);
private final static F<Integer> THREE = $(3);

private void demo() {
	LF2<Integer, Integer, Integer> add = new LF2<Integer, Integer, Integer>() {
		protected Integer lambda(Integer left, Integer right) {
			System.out.println("calc " + left + " + " + right);
			return left + right;
		}
	};
	LF3<Boolean, Integer, Integer, Integer> select = new LF3<Boolean, Integer, Integer, Integer>() {
		protected Integer lambda(Boolean arg1, Integer arg2, Integer arg3) {
			if (arg1) {
				return arg2;
			}
			return arg3;
		}
	};
	LF1<Integer, Integer> onePlus = add._(ONE);
	F<Integer> onePlusTwo = onePlus._(TWO);
	F<Integer> onePlusThree = onePlus._(THREE);
	System.out.println(select._(TRUE, onePlusThree, onePlusTwo)._());
}


运行结果

引用

calc 1 + 3

4


结果分析:
1、实现了Curry(利用匿名类,绑定参数)
2、实现了Lazy Evaluation(利用载入时字节码修改,会添加进参数是否已经被求值的判断)
3、类型安全

缺点:
匿名类的语法
调用语法(用_表示调用仍然会有很多括号)

12.9:
更新为最新的语法
   发表时间:2006-12-06  
SICP练习题4.25。定义了一个$函数把值封装成对象。定义了unless来做判断。定义了mul来做乘法。最后是把这些组装起来变成一个求阶乘的函数。如果这个函数不是lazy的话,必然导致堆栈溢出。

private static LF1<Integer, Integer> factorial = new LF1<Integer, Integer>() {
	protected Integer lambda(Integer n) {
		F<Integer> val1 = mul._($(n))._(factorial._($(n - 1)));
		F<Integer> val2 = $(1);
		return unless._($(n == 1))._(val1)._(val2)._();
	}
};
0 请登录后投票
   发表时间:2006-12-08  
添加了LazyStream,支持了无穷序列。下面是一个fibonacci数列的例子。和Haskell的版本没得比,但是俺已经竭尽所能了。cons(element1, fibs._(element2)._$(element1 + element2)),Java的语法能做到这样来表示无穷序列已经不容易了。另外谁能给一个和Haskell版本等价的实现?俺想不出来咋用zipWith等高阶函数来组装出来。貌似语法会更加丑陋。所有的源代码都在附件中。

private void demo() {
	LazyStream<Integer> stream = fibs();
	for (int i = 0; i < 10; i++) {
		System.out.println(stream.current());
		stream = stream.rest();
	}
}

private static LazyStream<Integer> fibs() {
	return fibs._(0, 1);
}

private static F2<Integer, Integer, LazyStream<Integer>> fibs = new F2<Integer, Integer, LazyStream<Integer>>() {
	public LazyStream<Integer> _(Integer element1, Integer element2) {
		return cons(element1, fibs._(element2)._$(element1 + element2));
	}
};
0 请登录后投票
   发表时间:2006-12-09  
关于Monad,俺也是现学。发觉拿Java做点函数式实验对于理解函数式语言还是很有帮助的。不过这Monad不知道弄对了没有。我也不是很确定。。。
Maybe Monad,用于减少null checking。只要有一个是null,就停止求值。下面是这个Monad的定义。
public abstract class Maybe<T> implements Monad {

	public final T value;

	private Maybe(T value) {
		this.value = value;
	}

	public static <T1, T2> Maybe<T2> bind(Maybe<T1> m, F1<T1, Maybe<T2>> f) {
		return (Maybe<T2>) m.bind(f);
	}

	public static <T1, T2, T3> Maybe<T3> bind(Maybe<T1> m, F1<T1, Maybe<T2>> f, F1<T2, Maybe<T3>> g) {
		return bind(bind(m, f), g);
	}

	public static <T> Maybe<T> maybe(T value) {
		return new Maybe<T>(value) {
		};
	}

	@SuppressWarnings("unchecked")
	public Monad bind(F1 f) {
		if (value == null) {
			return new Maybe(null) {
			};
		}
		return (Monad) f._(value);
	}
}

这是一个使用的例子。第一行打印nothing to say...第二行打印null
public class MaybeDemo {
	
	public static void main(String[] args) {
		new MaybeDemo().demo();
	}

	private void demo() {
		Maybe<String> result = (Maybe<String>) exec(maybe(""), remark1, remark2);
		System.out.println(result.value);
		System.out.println(bind(maybe("silence"), remark1, remark2).value);
	}

	private F1<String, Maybe<String>> remark1 = new F1<String, Maybe<String>>() {
		public Maybe<String> _(String words) {
			if ("".equals(words)) {
				return maybe("be nice, man");
			}
			return maybe(null);
		}
	};
	
	private F1<String, Maybe<String>> remark2 = new F1<String, Maybe<String>>() {
		public Maybe<String> _(String words) {
			return maybe("nothing to say...");
		}
	};
}
0 请登录后投票
   发表时间:2006-12-10  
突然发觉curry的写法和keyword message很相近啊。比如说我定义了一个moveFromTo的方法用来把一个元素从一个序列移动到另外一个序列。用keyword message可以写成move aObject :from aList :to anotherList。那么我用Java定义这么一个函数:
private F3<String, List<String>, List<String>, Nothing> moveFromTo = new F3<String, List<String>, List<String>, Nothing>() {
	public Nothing _(String element, List<String> fromList, List<String> toList) {
		fromList.remove(element);
		toList.add(element);
		return nothing;
	}
};

可以写成
moveFromTo._(aObject)._(aList)._(anotherList)。显然不是很好看的说。所以祭出cglib来,搞一搞。我希望能够这么写:
List<String> list1 = list("hello", "hi");
List<String> list2 = list("hi");
move("hello").from(list1).to(list2);

所以要定义一下相关的keyword接口:
private MoveKeyword move(String element) {
   return null; //暂时还没实现。
}
	
public static interface MoveKeyword {
	MoveFromKeyword from(List<String> fromList);
}
	
public static interface MoveFromKeyword {
	Nothing to(List<String> toList);
}

一般的做法是自己手写每个keyword接口的实现。但是我们已经有一个function啦(moveFromTo)。所以我只要拿cglib根据这个function和传给move的参数(element),就能自动帮我把这几个keyword接口给实现了。写法是:
return implement(MoveKeyword.class, moveFromTo, element);

implement是import static进来的。华丽一点可以做到implement(MoveKeyword.class).accordingTo(moveFromTo).with(element);。
代码在前面的压缩包里。
0 请登录后投票
   发表时间:2006-12-10  
Maybe Monad,谈Monad的入门资料,必定谈它。

首先看代码,比如定义了如下函数:
f::Int->Int
f  x = 10 `div` x  

g::Int->Int   
g x = x+1  

h = g.f 
当计算:
(1)h 2:正常;
(2)h 0:程序中断,除数不能为0
此时f的返回值,传给g出现了脱节,导致不匹配。此时怎么办,于是想到了搞一个Maybe type construtor,对它们进行统一封装,使得计算可以正常流动起来。就算是除0操作,也被视为了正常,达到了统一 。统一之后,就不会出现计算中断了。
使用用Maybe统一之后的代码:
f::Int->Maybe Int
f x = if x==0 then Nothing else Just (10 `div` x)

g::Maybe Int->Maybe Int
g Nothing = Nothing
g (Just x) = Just (x+1)

h = g.f
看到上面没有,采用Maybe之后,还是有不爽的地方,就是总是要对Maybe的值进行条件判断,看它是Nothing还是Just。一两个函数组合,问题还不大,当函数多了的时候,就麻烦了,很多的重复代码,那可是bad smell。于是对Maybe类型整了return和>>=两个函数,使其变成Monad,这样一切就好办啦。
此时的代码变成了如下的样子:
f::Int->Maybe Int
f x = if x==0 then Nothing else Just (10 `div` x)

g::Int->Maybe Int
g x = Just (x+1)

h x= (Just x)>>=f>>=g
这样,在g函数中,就无需再进行判断了,这些工作都交给了Monad。
0 请登录后投票
   发表时间:2006-12-10  
monad应该比你说的还要广一些。按照ajoo的说法,monad是一种计算模型。还有一种更抽象的是arrow啥的。按照我来看就是通过类型系统的限定(由于f的返回值是Maybe Int不是Int了),迫使程序员不能直接拿f的返回值去调用g。必须拿bind来把f和g“绑定”到一起去。不然你不知道该给g传啥参数,因为f的返回值装载Maybe这个“盒子”里呢。由于我认为Maybe是一个泛型的盒子。随意就把Maybe做成了Maybe<T>。
0 请登录后投票
   发表时间:2006-12-14  
Maybe Monad的应用只限于简单的情况:首先如果可选择的情况多于两种(也就是例外情况多于一种)在计算的每一步都要处理这些选择很麻烦,其次这样把正常逻辑和处理违例的逻辑混在一起,不是个best practice。
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics