优雅的函数式编程语言

快排,任何算法基础教程里必讲的最后一种排序算法,我这个差生直到毕业也没能用java或c默写出来一个快排。我模糊的直到他是一种“分而治之”的思想,可是一写到分而治之的时候就脑子里一片浆糊,搞不清分到哪了。那冗长的代码我想死记硬背也背不下来。重温一下噩梦,贴一个网上搜出来的java实现的快排:

public int getMiddle(Integer[] list, int low, int high) {
		int tmp = list[low];
		while (low < high) {
			while (low < high && list[high] > tmp) {
				high--;
			}
			list[low] = list[high];
			while (low < high && list[low] < tmp) {
				low++;
			}
			list[high] = list[low];
		}
		list[low] = tmp;
		return low;
	}
public void _quickSort(Integer[] list, int low, int high) {
		if (low < high) {
			int middle = getMiddle(list, low, high);
			_quickSort(list, low, middle - 1);
			_quickSort(list, middle + 1, high);
		}
	}
public void quick(Integer[] str) {
		if (str.length > 0) {
			_quickSort(str, 0, str.length - 1);
		}
	}

??

OK,这不是java算法的讨论帖,很显然,我贴这么多是用来黑java的。具体的一会儿接着黑。

继续我悲惨的故事,因为写不出快排,我不知被多少公司在笔试时直接拒掉,直到我从不考算法的小公司积累了经验并且弃暗投明去做前端。那我什么时候才能写出来快排了呢?换一种语言写的时候。

我学haskell的时候又看到了快排这一节,我皱了皱眉头,但是看到代码的时候,我惊呆了,让我吃惊的不是代码多简洁,这我早猜到了,让我吃惊的是快排的思路原来是如此简单。上haskell代码:

qs‘ [] = []
qs‘ (x:xs) =
	let lower = qs‘ . filter (< x) $ xs;
		higher = qs‘ . filter (>= x) $ xs
	in lower ++ [x] ++ higher

五行!当然,我说了让我吃惊的不是五行,而是我看到这段代码只是描述了一下什么是快排的分而治之,至于具体怎么样分而治之似乎根本就不是它的重点。什么叫优雅?优雅就是别人在那苦命工作的时候你点根烟慢悠悠的漫不经心的拿出了成果。

这些符号有些过于简单,我还是稍微解释一下便于没有接触过haskell的同学们理解。qs’是函数名,后面紧跟参数约定,等号后面是对函数的定义,可以理解为这是两个重载的函数。对于空列表就直接返回空列表了;对于有内容的列表,它的形式分解为头加尾(头是第一个元素,剩下的是尾),下面let里是分别对比对头元素小的列表和大的列表的定义,都是把qs函数和过滤函数频道一起,对$后面的参数进行调用,(<x)也是函数,意思很明显,就是判断是否比x小,把符合条件的过滤出来,递归地对它继续执行qs’。最后把小列表,中值(也就是头元素)和大列表拼起来。

对吧,基本就是描述了一下这个算法,没有看到java代码里把一个什么样的条件的元素移到什么地方,那玩意儿是最容易出错的。

然后看看它为什么可以用这么简洁的方式表达。作为对比,看看其它语言的实现。
我用coffeescript来写,也很容易就写出来了

qs = (list) ->
  if !list? || !list.slice || !list.length
    return list
  first = list[0]
  rest = list.slice 1
  lower = qs rest.filter (x) -> x < first
  higher = qs rest.filter (x) -> x >= first
  lower.concat(first).concat higher

多了点东西,多了什么呢?coffee里面不但需要判空,还要判断参数类型,因为coffee是动态类型语言;然后头尾需要单独来取一下,因为coffee没有解构。haskell代码里参数上的(x:xs)实际上是解构,也就是顺便就把参数给分解成好用的形式了。后面的行数一样,不过coffee多了个匿名函数,就是(x)->x<first,尽管coffee已经把函数写得很简单了,但还是没有haskell写成(<first)那么优雅,(<x)这个形式叫不全调用,是柯里化的成果(柯里化过两天专门说)。总的来说coffee还是比较优雅的。再跟js比一下:

function qs(list) {
  if (!list || !list.slice || !list.length)
    return list;
  var first = list[0];
  var rest = list.slice(1);
  var smaller = qs(rest.filter(function(x) {
    return x < first;
  }));
  var bigger = qs(rest.filter(function(x) {
    return x >= first;
  }));
  return smaller.concat(first).concat(bigger);
}

js的代码又长了一点。那些语法糖上的区别就不说了,主要一点是函数需要显示声明返回值,而不像coffee那样默认返回最后一句的结果。看起来只是多个return的问题,实际上这是一个“一切皆为表达式”的思想,这是一个更贴近于函数式编程的思想,因为函数式编程没有副作用,只能返回值,所以一切都得是表达式。

其实到此为止,js的版本其语法元素和整体思路都没有比haskell的版本差太多。但是,如果我开始没有看过haskell的版本,我肯定会照着java的那个思路去写,因为js具有函数式编程的能力,看你怎么用,它只是极度灵活,你用成什么样就是什么样,而且会差很多,这也正是js的魅力所在。不过没有好的引导很容就用成java那样了,记得看过一本写javascript设计模式的书,就是想方设法把js写成java,我没读下去。也正因如此,好好的学一门优秀的函数式编程语言对js的开发功力应该是大有裨益的。

回过头来继续黑java(java程序员请淡定,其实我也是java程序员出身,不久前我的正式职位还是“java开发工程师”)

同样,看看java的版本里多了些什么。我费了很大的定力总算是把这段代码大概看完了。_quickSort这个方法和haskell的整体差不多,都是要表达递归地分而治之。不过java似乎比较在意节约使用内存,整个代码没有返回新的列表,一直是在参数传进去的那个列表上调整,找中值的这个步骤就简直了。。。跟冒泡差不多吧。如果不在传入的列表内部鼓捣,也向前面那几种语言的版本每次过滤出新的小列表呢?是可以吧swap的步骤省了,但是过滤,怎么过滤?前面那几个都是传入一个函数(甚至是不全调用产生的超简单函数)最为过滤器的引擎,java没有内部函数(java8什么样我不知道),悲剧了,要不写个循环一个一个往出挑,要不就写个内部类。。。我想起了当年学写swing的时候满眼内部类的情景。。。

还没黑完,haskell、coffee和js的版本功能是等价的,而java版本的功能是被阉割的。人家排序的对象是任何列表,java这个版本里只能对整数数组排序。可以用泛型和接口吗?我记得好像有个叫Compareable的接口。。。算了,总之我在网上搜了半天无一例外都是对整形数组排序的,这只能说明用java处理通用类型是很痛苦的,为了简化都懒得写出来。

Haskell也是强静态类型语言,为啥代码里都感觉不到类型的存在呢?

优雅就是这样,举重若轻。

时间: 2024-10-25 07:59:00

优雅的函数式编程语言的相关文章

Scala与Clojure函数式编程语言的逆袭

编程世界就好比江湖,各种技术与思想有如各种内外家功夫在历史的舞台上纷呈登场,各领风骚.如今,自C.C++传承而来的以Java为代表的命令式语言一派可谓如日中天.门徒万千.多年来,这几门语言一直占据着TIOBE编程语言排行榜前几名,而很多"没落"的语言却在最近这几十年里逐渐淡出了人们的视线.在命令式语言中,随着面向对象编程的流行而兴起的对设计模式的探讨始终是OO程序员群体中的热门话题,设计模式的相关问题也一度成为面试官遴选候选者的硬性指标. 然而,江湖上逐渐出现了一种说法,"有

函数式编程语言

最近一段时间总是听到或者看到有人谈论“函数式编程”,第一次接触是在大概半年前的一次沙龙中,当时听人讲的时候,心想这有什么难理解的,函数式编程,函数嘛,那就是C呀,C++放在首位的是对象,面向对象编程,C放在首位的不是函数吗,那就是函数式编程啊,整个经验交流迷迷糊糊的,只记住了个lambda.但是在后来陆陆续续还是听到了这个名词,而且也知道并不是我理解的那个样子,C不是“函数式编程”,而是“面向过程编程”,是“命令式编程”,但是还不是很理解到底是怎么区分的.于是周末翻阅了一下资料,稍作“概念层次”

[原创译书] JS函数式编程 2.1 函数式编程语言

?? Functional Programming in Javascript 主目录第二章 函数式编程基础 函数式编程语言 函数式编程语言是那些方便于使用函数式编程范式的语言.简单来说,如果具备函数式编程所需的特征, 它就可以被称为函数式语言.在多数情况下,编程的风格实际上决定了一个程序是否是函数式的. 是什么让一个语言具有函数式特征? 函数式编程无法用C语言来实现.函数式编程也无法用Java来实现(不包括那些通过大量变通手段实现的近似函数式编程). 这些语言不包含支持函数式编程的结构.他们是

Frege-基于JVM的类Haskell纯函数式编程语言

Frege是一门受Haskell语言启示而设计的纯函数式编程语言.Frege程序会被编译为Java,并执行于JVM上.它与Haskell是如此的类似.以至于有人称它为JVM上的Haskell.取Frege这个名字是为了纪念德国数学家.逻辑学家.哲学家Gottlob Frege. 语言的主要特色 纯函数式编程语言 继承了函数式编程语言Haskell的精神.默认就具有不变性(immutability). 执行于JVM之上 Frege程序编译为Java bytecode.执行于JVM(Java 7+)

对函数式编程语言的理解

1.什么是函数式语言? 函数式语言(functional language)一类程序设计语言,是一种非冯·诺伊曼式的程序设计语言.函数式语言主要成分是原始函数.定义函数和函数型.这种语言具有较强的组织数据结构的能力,可以把某一数据结构(如数组)作为单一值处理:函数式编程语言最大的特点就是函数可以作为参数.结果也可以是函数,这种定义的函数称为高阶函数,程序就是函数,程序作用在结构型数据上,产生结构型结果,从根本上改变了冯·诺伊曼式语言的“逐词”工作方式. 函数式编程(英语:functional p

函数式编程语言LISP,python,haskell,clojure

说说我自己的背景吧,我是个半吊子的程序员,做任何事情喜欢比较了解然后再尝试,我接触过很多语言,大多数都把它当成工具来使用 我现在的工作大部分主要在于数据挖掘与机器学习方面,也学习web开发,我第一个拿来工作的语言是python,我是从那里得知函数式编程的概念的 说起lisp据说python就是模仿的lisp,而得知这个语言是从黑客与画家这本书中得知的,据说是一个特别值得尝试的语言,由于作者说如果年龄超过25岁,最好不要尝试学习它,我明年就本命年了,看来应该试着尝试一下了 在之前面临招工作的时候曾

新函数式编程语言将简化GPU应用开发

哥本哈根大学计算机科学系的研究人员最近发布了开源函数编程语言 Futhark,为 GPU 应用程序生成C和 Python 代码,加速机器学习和数学密集类程序的开发.绝大多数 GPU 编程使用到框架如 OpenCL 或 CUDA,两个框架都使用C或 C++ 方言去生成运行在 GPU 上的代码. Futhark 是用 Haskell 开发的,语法上也类似 Haskell 或 Standard ML 语言,能自动生成能整合在现有应用中的 C 和 Python 代码.开发者声称这种新语言简化了使用并行计

[原创译书] JS函数式编程 2.2 与函数共舞

?? Functional Programming in Javascript 主目录第二章 函数式编程基础上一节 函数式编程语言 与函数共舞 有时,优雅的实现是一个函数.不是方法.不是类.不是框架.只是函数. - John Carmack,游戏<毁灭战士>首席程序员 函数式编程全都是关于如何把一个问题分解为一系列函数的.通常,函数会链在一起,互相嵌套, 来回传递,被视作头等公民.如果你使用过诸如jQuery或Node.js这样的框架,你应该用过一些这样的技术, 只不过你没有意识到. 我们从J

从Storm和Spark Streaming学习流式实时分布式计算系统的设计要点

0. 背景 最近我在做流式实时分布式计算系统的架构设计,而正好又要参见CSDN博文大赛的决赛.本来想就写Spark源码分析的文章吧.但是又想毕竟是决赛,要拿出一些自己的干货出来,仅仅是源码分析貌似分量不够.因此,我将最近一直在做的系统架构的思路整理出来,形成此文.为什么要参考Storm和Spark,因为没有参照效果可能不会太好,尤其是对于Storm和Spark由了解的同学来说,可能通过对比,更能体会到每个具体实现背后的意义. 本文对流式系统出现的背景,特点,数据HA,服务HA,节点间和计算逻辑间