论坛首页 Java企业应用论坛

Java 语言中的函数编程

浏览 69708 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-09-17  
值得阅读,有深度,深有所得。
楼主的不辞辛苦的介绍为我等打开了一扇新的窗户。感激不尽,建议加精。
0 请登录后投票
   发表时间:2004-09-17  
恩,既然已经讲到了Monads,看来不讲一下Y Combinator,fix point,总觉得有些对不起大家。但是考虑到这个问题是出了名的累死人不陪命....。这样吧我出一个小题目,看看大家能不能够看懂,如果看得懂的人多呢就讲了,如果少的话就不说了。

def cfold1(fun,z,list);:    
    if(len(list);==0);:
       return z
    else:        
       return fun(list.pop();,z,lambda y:cfold1 (fun,y,list););
def cfold(Operator,z,l);:
   return  cfold1(lambda x,t,g:Operator(x,g(t););,z,l);
if __name__=="__main__":      
    print cfold(lambda x,y:x+y,0,[1,2,3,4]);            

我现在已经能够想象到屏幕前面对这个程序时你的抓狂程度。  
0 请登录后投票
   发表时间:2004-09-17  
Trustno1 写道

不考虑数据,是算法的算法
递归是最主要的逻辑结构

在FP里面我们可以把函数当作抽象的逻辑,这些逻辑独立于数据。然后对这些逻辑进行编程组合,得到一个新的逻辑或者算法。FP也可以说成是针对逻辑的编程,而Java/C++这些可以说是针对数据的编程。

因此我们可以看到其实算子空间本身具有的性质就是N维正交。也就是说每个算子都是独立互不干扰的。他不会有OO那样牵扯不清的各种问题。

而FP不一样,他所有的代码都是一片一小片的片断,首先这些小片段的改动将比动整个金字塔来的更加容易,其次这些小片断天生就是独立正交不相互干扰,我修改了mother,不会干扰father,而OO要做到这样就要动用接口,模式这种非常笨重的方法。

几个问题:
1.上面的例子中没有明显的感觉到"不考虑数据"这个特性!我所理解的是不考虑数据,不光是不考虑数据的值,当然也包括数据的类型。不知具体指的什么?(也可能是我思考不够深入,原谅)
2.我学编译的时候只学过用if和for实现递归,倒从来没考虑过怎么用递归来实现if和for,看来FP把他们都放到表达式一层了。不知我的理解是否正确?
3.我对算子空间的理解本来是一个包含不同映射关系的集合,可后面解释的时候似乎又变成了值域的集合,有点糊涂啦。我觉得还是说映射关系的集合更好一点,因为值域本身就是一个集合啦。虽然意思一样,但理解起来容易混淆。

下面给出一段用java实现的“独立互不干扰”的代码片断,用了decorator pattern,例子来自 Steve Metsker的Design Patterns Java Workbook (http://www.oozinoz.com) 别说我做广告哦:wink:
哪位感兴趣的话代码可以去上面的网址下载
这个是decorator的核。
package com.oozinoz.function;

/*
 * Copyright (c); 2001 Steven J. Metsker.
 * 
 * Steve Metsker makes no representations or warranties about
 * the fitness of this software for any particular purpose, 
 * including the implied warranty of merchantability.
 *
 * Please use this software as you wish with the sole
 * restriction that you may not claim that you wrote it.
 */
/**
 * This abstract superclass defines the role of a function
 * that wraps itself around (or "decorates"); another function.
 * 
 * @author Steven J. Metsker
 */
public abstract class Function 
{
	protected Function[] source;

/**
 * Construct a function that decorates the provided source
 * functions.
 *
 * @param Function[] the source functions that this function
 *                   wraps
 */
public Function(Function[] source);
{
	this.source = source;
}
/**
 * Construct a function that decorates the provided source
 * function.
 *
 * @param Function the source function that this function
 *                 wraps
 */
public Function(Function f);
{
	this(new Function[] { f });;
}

/**
 * Return true if these functions are of the same class
 * and their source functions are equal.
 *
 * @return true if these functions are of the same class
 *         and their source functions are equal
 */
public boolean equals(Object o);
{
	if (!getClass();.equals(o.getClass();););
	{
		return false;
	}
	Function f = (Function); o;
	for (int i = 0; i < source.length; i++);
	{
		if (!source[i].equals(f.source[i]););
		{
			return false;
		}
	}
	return true;
}

/**
 * Apply this object's represented function to the source
 * function(s); at the given time and return the result.
 *
 * @param t the time function that goes 0 to 1 and that
 *          other functions use as a parameter
 *
 * @return the result of applying this object's represented
 *         function to the source function(s); at time t
 */
public abstract double f(double t);;

/**
 * Return a textual representation of this function.
 *
 * @return a textual representation of this function
 */
public String toString();
{
	String name = getClass();.getName();;
	String name2 = name.substring(name.lastIndexOf('.'); + 1);;
	StringBuffer buf = new StringBuffer(name2);;
	if (source.length > 0);
	{
		buf.append('(');;
		for (int i = 0; i < source.length; i++);
		{
			if (i > 0);
			{
				buf.append(", ");;
			}
			buf.append(source[i]);;
		}
		buf.append(');');;
	}
	return buf.toString();;
}
}

下面是两个独立的算子
package com.oozinoz.function;

/*
 * Copyright (c); 2001 Steven J. Metsker.
 * 
 * Steve Metsker makes no representations or warranties about
 * the fitness of this software for any particular purpose, 
 * including the implied warranty of merchantability.
 *
 * Please use this software as you wish with the sole
 * restriction that you may not claim that you wrote it.
 */
/**
 * Wrap the Math.abs(); function around a given source.
 * 
 * @author Steven J. Metsker
 */
public class Abs extends Function 
{

/**
 * Construct an absolute value function that decorates the 
 * provided source function.
 *
 * @param Function the source function that this function
 *                 wraps
 */
public Abs(Function f);
{
	super(f);;
}
/**
 * Return Math.abs(); applied to the source function's value
 * at time t.
 *
 * @param t the time function that goes 0 to 1 and that
 *          other functions use as a parameter
 *
 * @return Math.abs(); applied to the source function's value
 *         at time t.
 */
public double f(double t);
{
	return Math.abs(source[0].f(t););;
}
}


package com.oozinoz.function;

/*
 * Copyright (c); 2001 Steven J. Metsker.
 * 
 * Steve Metsker makes no representations or warranties about
 * the fitness of this software for any particular purpose, 
 * including the implied warranty of merchantability.
 *
 * Please use this software as you wish with the sole
 * restriction that you may not claim that you wrote it.
 */
/**
 * Wrap an arithmetic function around a pair of given sources.
 * 
 * @author Steven J. Metsker
 */
public class Arithmetic extends Function 
{
	protected char operator;
/**
 * Construct an arithmetic function that decorates the 
 * provided source functions.
 *
 * @param char the operator to apply (+ - * /);
 *
 * @param Function the first source function that this
 *                 function wraps
 *
 * @param Function the second source function 
 */
public Arithmetic(
	char operator,
	Function f1,
	Function f2);
{
	super(new Function[] { f1, f2 });;
	this.operator = operator;
}
/**
 * Return the result of applying this objects' arithmetic 
 * operator (+ - * /); to the source functions' values
 * at time t.
 *
 * @param t the time function that goes 0 to 1 and that
 *          other functions use as a parameter
 *
 * @return the result of applying this objects' arithmetic 
 *         operator (+ - * /); to the source functions' values
 *         at time t
 */
public double f(double t);
{
	switch (operator);
	{
		case '+' :
			return source[0].f(t); + source[1].f(t);;
		case '-' :
			return source[0].f(t); - source[1].f(t);;
		case '*' :
			return source[0].f(t); * source[1].f(t);;
		case '/' :
			return source[0].f(t); / source[1].f(t);;
		default :
			return 0;
	}
}
}

说实话,我并不觉得算子实现起来特别的复杂,当然,比FP是不如的。不过我觉得fp的长处似乎在于科学计算,处理业务逻辑的时候,我还是更希望看到结构明晰的处理逻辑,而不是简单的表达式,会让我这种智商的人犯晕(已经超过80了,算正常人)。任何语言都有它的目标,硬要拿长处和短处比是不公正的。
还是很感谢Trustno1带给我这么精彩的内容,我是觉得很有深度啦
0 请登录后投票
   发表时间:2004-09-17  
引用

1.上面的例子中没有明显的感觉到"不考虑数据"这个特性!我所理解的是不考虑数据,不光是不考虑数据的值,当然也包括数据的类型。不知具体指的什么?(也可能是我思考不够深入,原谅)

数据的值,是需要考虑的,数据的类型在Haskell这样的强类型FP中同样要考虑。我说的不考虑数据的意思是不考虑数据的状态维护。

引用

2.我学编译的时候只学过用if和for实现递归,倒从来没考虑过怎么用递归来实现if和for,看来FP把他们都放到表达式一层了。不知我的理解是否正确?

FP不是表达式,而是一系列的规则。例如你用Java写一个Fibonacci的递归函数其实就是一个FP,只是他没有用FP语言罢了。至于流式控制和递归之间的转换问题,请看这个帖子。
http://forum.iteye.com/viewtopic.php?t=6551&highlight=
递归的表达要比imperative的要好,而且速度和效率等价于imperative语言。

引用

3.我对算子空间的理解本来是一个包含不同映射关系的集合,可后面解释的时候似乎又变成了值域的集合,有点糊涂啦。我觉得还是说映射关系的集合更好一点,因为值域本身就是一个集合啦。虽然意思一样,但理解起来容易混淆。

你可以这样理解,{f(x)|x-&gt;{g(x),g1(x),g2(x)}}。这里后面那个集合就是一个算子空间,每个g(x)就是一个算子。
至于那个Java实现,我就不多说了。在Java里面实现这种东西,没有多大的意义。很多功能都不能做,就是能做也是麻烦的要死。模拟FP是一回事情,正真的FP是另外一回事情。你可以去ABP看看,哪里有个搞ASM的家伙,用MASM模拟
OO而且模拟的还不赖,但是你就不能说汇编语言是OO语言吧。
要在Java中实现FP唯一的一种途径是使用基于JVM的规则语言,例如JSR-94里面的几个候选标准,说不定groovy也可以但是我没有学过不好说。

至于FP有没有用这个问题,关键是看你在那里用。其实我们经常使用XSLT就是一种FP,再说了XSLT还不是再处理Business Logic,而是UI。至于业务逻辑的处理用哪个好,如果你不是很复杂的逻辑,那么用那个都无所谓。就像你高中时后的运动学方程不会用到相对论一样,虽然相对论也能推出牛顿定律但那个东西只能是大炮打蚊子。如果逻辑非常复杂的例如CRM中的一些数据过滤等等,一般都采用规则引擎,如果要用imperative的代码根本没法写。这方面BPM是应用最广泛的地方,JSR-94就是为了简化BPM而实现的规则引擎.你可以去读读JSR-94的规范,你会发现我们通常写的那些OO对象在这个规范里面就变成了一个个的算子。然后通过这个规范的规则将他们粘合起来。而这种粘合的规则实际上就是一种FP。Osis的RML和BRML 也是一种FP,只不过用XML标记描述罢了.
0 请登录后投票
   发表时间:2004-09-17  
Trustno1 写道
恩,既然已经讲到了Monads,看来不讲一下Y Combinator,fix point,总觉得有些对不起大家。但是考虑到这个问题是出了名的累死人不陪命....。这样吧我出一个小题目,看看大家能不能够看懂,如果看得懂的人多呢就讲了,如果少的话就不说了。

def cfold1(fun,z,list);:    
    if(len(list);==0);:
       return z
    else:        
       return fun(list.pop();,z,lambda y:cfold1 (fun,y,list););
def cfold(Operator,z,l);:
   return  cfold1(lambda x,t,g:Operator(x,g(t););,z,l);
if __name__=="__main__":      
    print cfold(lambda x,y:x+y,0,[1,2,3,4]);            

我现在已经能够想象到屏幕前面对这个程序时你的抓狂程度。  


为了看懂这个,偶特地去学习了一把python的一些相关的基本语法,比如lambda,比如list.pop(),在纸上画了好一会儿,头发都掉了好几根,推了几步,归纳了一下,答案是4+3+2+1+0吧,要是错了,我真要考虑买块豆腐撞几下了。
0 请登录后投票
   发表时间:2004-09-17  
to:mochow大姐

我和你差不多,为了搞懂这个程序,我不但学习了语法,而且还特地下载了一个python。

我的理解是:
print cfold(lambda x,y : x+y,0,[1,2,3,4])

是交给cfold一个计算任务。
0是初始值
[1,2,3,4]是计算序列
lambda x,y : x+y就是计算的函数。

如果给他的是lambda x,y : x*y
而且初始值得是1
那么得到的就是1*1*2*3*4=24

Trustno1,可以接着讲了吧。
0 请登录后投票
   发表时间:2004-09-17  
把你们的推导过程贴出来吧。
0 请登录后投票
   发表时间:2004-09-17  
Trustno1 写道
把你们的推导过程贴出来吧。


偶这个是土办法

计算cfold(lambda x,y : x+y,0,[1,2,3,4])           
第一步,要弄清lambda x,y : x+y是说啥的,去网上google一把得出
         它的意思是 f1=f(x,y)= x+y;
第二步,得出要打印cfold(f1,0,[1,2,3,4]) 

第三步,由定义cfold(Operator,z,l)得出
    Operator = f1 =f(x,y) = x+y
       z =0;
       l = [1,2,3,4]
       lambda x,t,g:Operator(x,g(t)) 实际上就是f(x,t,g) = x+g(t);

       则有:
    cfold1(lambda x,t,g:Operator(x,g(t)),z,l) = cfold1(x+g(t),0,[1,2,3,4])

第四步, 开始计算cfold1(x+g(t),0,[1,2,3,4])
由该函数的定义知 fun的形式为 fun(x,t,g) = x+g(t);
      
       list长度不为0,list.pop()为4,
       lambda y:cfold1 (fun,y,list)则为f(y) = cfold1 (fun,y,[1,2,3])
       则fun(4,0,cfold1 (fun,y,[1,2,3])) = 4+cfold1 (fun,0,[1,2,3])

第五步,计算4+cfold1 (fun,0,[1,2,3])和第四步差不多,结果是
          4+3+cfold1 (fun,0,[1,2])
...
最后当list为空的时候返回0则结果应该是4+3+2+1+0
0 请登录后投票
   发表时间:2004-09-17  
1、cfold(lambda x,y : x+y,0,[1,2,3,4])
  cfold(f(x,y)=x+y,0,[1,2,3,4])

2、cfold(Operator,z,l)
  Operator = f(x,y)=x+y
  z = 0
  l = [1,2,3,4]

  lambda x,t,g : Operator(x,g(t)) = f(x,g,t)=x+g(t)
  cfold1(lambda x,t,g : Operator(x,g(t)),z,l)
cfold1(f(x,g,t)=x+g(t),z,l)

3、cfold1(fun,z,list)
  fun = f(x,g,t)=x+g(t)
  z = 0
  list = [1,2,3,4]

  if(len(list)==0): //这是最后一步,在递归的最后
    return z //发现数列空了之后,就返回z,也就是初始值

  else
    return fun(list.pop(),z,lambda y:cfold1 (fun,y,list))

4、fun(list.pop(),z,lambda y:cfold1 (fun,y,list))
  fun = x+g(t)
  x = list.pop()
  t = z
  g = f(y)=cfold1 (fun,y,list)

  这个函数的含义就是:
    list.pop()+cfold1(fun,z,list)

5、到这里以后,就可以看清楚一个递归了
  cfold1 = fun(list.pop(),z,lambda y:cfold1 (fun,y,list))
  fun = x+g(t)
  g = cfold1 (fun,y,list) //list已经被取走一个数了

6、在回过头去看cfold(Operator,z,l)的作用,
  我们给cfold的算法是f(x,y)=x+y
  但是cfold需要把他拼装成x+g(y),这样才能实现递归的计算。

我相信自己已经说清楚了。
0 请登录后投票
   发表时间:2004-09-17  
似乎还可以简单点,当得出fun(x,t,g)=x+g(t)的时候,
函数cfold1的返回fun(list.pop(),z,lambda y:cfold1 (fun,y,list)) 可以简化为list.pop()+cfold1 (fun,z,list),就可以看的更清楚了。
0 请登录后投票
论坛首页 Java企业应用版

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