`
Lich_Ray
  • 浏览: 164092 次
  • 性别: Icon_minigender_1
  • 来自: 南京
文章分类
社区版块
存档分类
最新评论

函数式编程语言曲高和寡?

阅读更多
引用

看到作者 lichray 忙于研究数理逻辑,其父发出了由衷的感叹:你学的东西没人用啊。“谁说没人用?自己看不懂罢了。Haskell 的语法是‘写意’了点,但其中的思想清澈见底。”

引用

本文以一个函数式风格的快速排序算法为例,把它从 Haskell 代码改写为 大家所熟知的 JavaScript 代码,试图说明 FP 绝对是表达思想的最强工具。不要被那些 FP 语言们的语法所迷惑。终有一天,你会发现,原来这才是编程啊。


下面是一个 Haskell 写的快速排序算法,一共四行(想想学校里教的一般是多少行~):
qsort [] = []
qsort (x:xs) = qsort lt ++ [x] ++ qsort st
	where lt = [y | y <- xs, y < x]
		st = [y | y <- xs, y >= x]

想理解它是再简单不过的事:首先确定递归下界——排序空列表的结果为空列表本身。
qsort [] = []
然后把快速排序算法的目的描述一下:
把一个选出的元素(这里是第一个元素 x)作为“中间元素”,同时把剩余元素组成的列表记作 xs,将所有小于中间元素的元素组成的列表排序 qsort lt 连接上 ++ 这个元素“组成的列表”[x] 再连接上 ++ 所有大于等于这个元素的其它元素排序后的列表 qsort st;这里用到的 where 变量 it 指的是 = 所有小于它的元素组成的列表 [y | y <- xs, y < x],st 指的是 = 所有大于等于这个元素的其它元素的列表 [y | y <- xs, y >= x]。

下面把它转换为较低级的 Scheme 代码。先分析一下这里用到的 Haskell 语言的特性:
  1. 第一句,指定函数取值点。相对于数学中描述函数在某点取值的语法。对于普通的语言,可以用 if 语句代替。
  2. 参数 (x:xs),是参数领悟特性。指的是,此参数是一个由元素 x 开头,剩余部分为 xs 组成的列表。对于普通的语言,可以用在函数内部绑定参数的分解结果的方法代替。
  3. 列表连接运算符 ++,用 append 函数代替。
  4. where 语句,后置变量说明。用前置 let 绑定代替。
  5. 类似 [y | y <- xs, y < x] 的列表领悟。对于单元素的领悟,用 filter 函数过滤,过滤规则表示为 lambda 函数,它对于 y < x 返回 true。

下面是转换后的 Scheme 版本的程序:
;; 使用 SICP 中描述的 filter 函数,就不抄在这儿了
(define (qsort ls)
	(if (null? ls) '()
		(let
			((x (car ls))
			(xs (cdr ls)))
			(let 
				((lt (filter (lambda (y) (< y x)) xs))
				(st (filter (lambda (y) (>= y x)) xs)))
				(append (qsort lt) (list x) (qsort st))))))


下面把它转换为我们需要的 JavaScript 代码。先分析一下这里用到的 Scheme 语言的能力:
  1. 取列表首项函数 car,用 Array[0] 代替。
  2. 取列表剩余项函数 cdr,用 Array.slice(1) 代替。
  3. 判断列表是否为空函数 null?,用 ls == false 代替(JS 的特殊之处)。
  4. 变量绑定语法 let,仅用声明变量语法 var 代替(千万不能不用)。// 在 JavaScript 1.6 中加入了 let 关键字,爽一点;还有列表领悟,无语了!
  5. 过滤器函数 filter。为快一点起见,自己用命令式风格写一个绑定在 Array.prototype 上。
  6. 匿名函数 lambda,不就是匿名 function 嘛!
  7. 列表连接函数 append,用 Array.concat() 代替。

OK。下面是要用到的、对 JavaScript 1.5 作出的扩展:
// 把要用到的表达式抽象出来
Array.prototype.head = function () {
    return this[0];
}

Array.prototype.tail = function () {
    return this.slice(1);
}

Array.prototype.filter = function (proc) {
    var tmpArr = [];
    for (var i = 0; i < this.length; i++)
	if (proc(this[i]) == true)
	    tmpArr.push(this[i]);
    return tmpArr;
}

这样就可以写出转换后的 JavaScript 代码了:
Array.prototype.qsort = function () {
	if (this == false) return []
	var x, xs, lt, st
	x = this.head()
	xs = this.tail()
	lt = xs.filter(function (y) {return y < x})
	st = xs.filter(function (y) {return y >= x})
	return lt.qsort().concat([x], st.qsort())
}

最后试一下:
js> [4,7,9,1,3,5].qsort()
1,3,4,5,7,9
是不是有一种“终于找到组织了”的感觉呢?

题外话:
对比一下用命令式方法写成的 JavaScript 版的快速排序:
function qsort (arr, l, u) {
    l = l || 0;
    u = ((u != 0) && (u == undefined)) ? arr.length : u;
    if (l >= u) return;
    var m = l;
    for (var i = l+1; i <= u; i++)
	if (arr[i] < arr[l])
	    arr.swap(++m, i);
    arr.swap(l, m);
    qsort(arr, l, m-1);
    qsort(arr, m+1, u);
}

代码好像少一点,但我没看懂——虽然是我自己写的,3 个月前写的——要是有 bug 我就直接 faint 了...
JavaScript 真是一个站在函数式编程语言与命令式编程语言之间的奇特生物——包容任何思想,这也是我钟爱 JavaScript 的一个重要原因。

函数式编程语言曲高和寡,谁说的?掌握其思想是重要的,也是容易的;语言是其次,只是表达思想的工具罢了。
分享到:
评论
14 楼 yakamoz 2007-12-31  
function currying(func)   
{   
    return function()   
    {   
        var args = Array.prototype.slice.call(arguments,0);      
        if(args.length<func.length)   
        {                        
            return function(){   
                var _args = args.concat(Array.prototype.slice.call(arguments,0));      
                return currying(func).apply(this,_args);   
            }   
        }   
        else return func.apply(this,args);   
    }   
}  
13 楼 Trustno1 2007-08-03  
厌倦发呆 写道
oxware 写道
Elminster 写道
FP 并不适于描述算法,你看所有关于时间空间复杂度的严肃讨论都是在图灵机模型上展开的,就知道了。

FP远比图灵机模型适合描述算法
很多讨论都在图灵机展开,是因为人类目前的制造水平只能造出类似图灵机的冯诺依曼机器……或者说历史和产业的契机看中了冯诺依曼机器

所谓"算法",并非仅仅指若干数学公式,还包括该算法应用于不同问题时,其在空间和时间的成本花销的评估.FP适合描述公式,即如何求值,但描述时间和空间成本上的花销就不是很方便了。另外,非冯诺依曼计算机很早就存在,只不过没有推广开。

算法涉及两个方面:1.可计算性,2.复杂性.
对于第一个问题来说,不是所有的数学问题都是可计算的,在这个领域里,lambda caculus与图林机相比有优势.要证明数学问题是否可递归化,要比用图林机方便的多.但是这个领域基本上就是纯粹的数学分析,递归化只不过是一个证明的目标.FP恐怕连工具都算不上,仅仅是在最后实现算法的时候,用FP表述递归函数更简便一些.

第二个问题,基本上是图林系统了.
12 楼 厌倦发呆 2007-08-03  
oxware 写道
Elminster 写道
FP 并不适于描述算法,你看所有关于时间空间复杂度的严肃讨论都是在图灵机模型上展开的,就知道了。

FP远比图灵机模型适合描述算法
很多讨论都在图灵机展开,是因为人类目前的制造水平只能造出类似图灵机的冯诺依曼机器……或者说历史和产业的契机看中了冯诺依曼机器

所谓"算法",并非仅仅指若干数学公式,还包括该算法应用于不同问题时,其在空间和时间的成本花销的评估.FP适合描述公式,即如何求值,但描述时间和空间成本上的花销就不是很方便了。另外,非冯诺依曼计算机很早就存在,只不过没有推广开。
11 楼 oxware 2007-08-03  
Elminster 写道
FP 并不适于描述算法,你看所有关于时间空间复杂度的严肃讨论都是在图灵机模型上展开的,就知道了。

FP远比图灵机模型适合描述算法
很多讨论都在图灵机展开,是因为人类目前的制造水平只能造出类似图灵机的冯诺依曼机器……或者说历史和产业的契机看中了冯诺依曼机器
10 楼 mathgl 2007-07-18  
haskell是个不错的冬冬

正在学
9 楼 whisper 2007-07-18  
当年就是靠个haskell的快排把面试官忽悠过去的
现在我一说haskell,就接近被K
8 楼 coderplay 2007-07-15  
erlang用上list comprehension

qsort([]) ->
    [];
qsort([H | T]) ->
    qsort([ X || X <- T, X < H ]) ++ [H] ++ qsort([ X || X <- T, X >= H ]).
7 楼 dennis_zane 2007-07-15  
cookoo 写道
这个qsort实现partition时多搜了一次全表,性能差了点


ruby倒是直接支持partition,方便很多:
def qsort(array)
  arr= array.dup
  if arr==[]
    []
  else
    x=arr.shift
    smaller,bigger=arr.partition{|e| e<=x}
    qsort(smaller)+[x]+qsort(bigger)
  end
end


Erlang也可以改写下:
sort([]) -> [];
sort([Pivot|Rest]) ->
{Smaller, Bigger} = split(Pivot, Rest),
lists:append(sort(Smaller), [Pivot|sort(Bigger)]).
split(Pivot, L) ->
split(Pivot, L, [], []).
split(Pivot, [], Smaller, Bigger) ->
{Smaller,Bigger};
split(Pivot, [H|T], Smaller, Bigger) when H < Pivot ->
split(Pivot, T, [H|Smaller], Bigger);
split(Pivot, [H|T], Smaller, Bigger) when H >= Pivot ->
split(Pivot, T, Smaller, [H|Bigger]).

模式匹配就体现的更充分了。当然Erlang也是有支持partition,这样写出来就跟ruby差不多了
6 楼 Elminster 2007-07-15  
FP 并不适于描述算法,你看所有关于时间空间复杂度的严肃讨论都是在图灵机模型上展开的,就知道了。
5 楼 Lich_Ray 2007-07-15  
cookoo 写道
这个qsort实现partition时多搜了一次全表,性能差了点

这个确实。相比之下,命令式语言可以利用同时给两个数组赋值的办法解决这个问题。FP 中想这么干,用 unzip [(y,z) | y <- xs, z <- xs, y < x, z >= x] 倒是可以,但那样会用到矩阵转置,要不是因为有元组的访问效率的话,恐怕就得不偿失了。用 filter 也是类似的。看来只能修改 filter 了,使它返回一个由两个列表组成的元组,前者是过滤结果,后者是剩余结果。
part [] _ = ([], [])
part (x:xs) func =
	if func x
	then (x : fst (part xs func), snd (part xs func))
	else (fst (part xs func), x : snd (part xs func))

现在就可以令
(lt,st) = part xs (<x)

差不多了吧。感觉是可以了。part 函数中的 fst,snd 函数费时应该可以忽略不计。part 很有用的样子…如果写成 JavaScript 版,还可以利用一下基于对象模型,命令式的风格搞不好效率更高。
4 楼 cookoo 2007-07-15  
这个qsort实现partition时多搜了一次全表,性能差了点
3 楼 simohayha 2007-07-14  
dennis_zane 写道


有个疑问,qsort (x:xs)与qsort([H|T])应该都是模式匹配吧?我对haskell不了解


呵呵,恩 都是模式匹配。 我觉得FP中 的模式匹配,就是为了更好的使用递归.

PS:感觉有模式匹配的语言真是起来舒服的说,呵呵。

2 楼 tysw 2007-07-14  
只是一个习惯性问题, 看惯了命令式语言写的程序, 咋一看函数式的肯定不习惯
1 楼 dennis_zane 2007-07-14  
语言是其次,只是表达思想的工具罢了,说的好,来个Erlang版本的:
qsort([])->[];
qsort([H|T])->
    Lt=lists:filter(fun(E)->E<H end,T),
    St=lists:filter(fun(E)->E>=H end,T),
    lists:append(qsort(Lt),[H|qsort(St)]).


有个疑问,qsort (x:xs)与qsort([H|T])应该都是模式匹配吧?我对haskell不了解

相关推荐

    曲高和寡(成语故事)

    成语“曲高和寡”源自中国古代音乐美学思想,其故事出自《战国策·楚策四》中的“高山流水遇知音”。该成语表达了高深的音乐或者深奥的艺术品不为大众所理解,导致欣赏者寥寥无几的意境。接下来,我会深入阐述这一...

    汉语言文化类电视节目研究

    然而,传统的语言文化类节目常常遭遇曲高和寡的尴尬,无论是收视率还是影响力长期以来都处于弱势地位。 二、汉语言文化类电视节目的发展策略 (一)提高文化自觉程度 文化自觉是一种价值观的选择,包含了一种文化...

    当信息化遭遇“曲高和寡”..pdf

    当信息化遭遇“曲高和寡”..pdf

    423·读书月丨名家名著知识问答挑战赛 关键词搜索

    423·读书月丨名家名著知识问答挑战赛 txt文档,ctrl+F关键词...“曲高和寡”的典故和哪位辞赋家有关? 宋玉 毛姆的小说《月亮和六便士》取材于哪位法国印象派画家的生平? 保罗·高更 古今第一善书 太上感应篇 巨人传

    巨野2016年事业编招聘考试真题及答案解析整理版.docx

    同样,选项D中"曲高和寡"比喻作品高雅而很少有人能欣赏,而"雅俗共赏"则表示能被不同层次的人群都喜欢,形成了对比关系。 5. 这道题是动作与工具的匹配。"投掷"对应"长矛","射击"对应"手枪",以此类推。所以,选项...

    六年级语文_上__回顾拓展八.ppt

    此外,文档还列举了一系列成语,如“脍炙人口”、“不落窠臼”、“曲高和寡”等,这些成语不仅展示了中华文化的博大精深,也是对学生词汇积累和文化素养的提升。它们代表了不同风格和层次的艺术表现,有助于学生理解...

    悟成语魅力,享艺术教学

    例如,从“响遏行云”中理解秦青的高超技艺,从“曲高和寡”和“阳春白雪”中领悟高雅艺术的含义。通过这种方式,学生不仅能积累成语,还能深化对成语内涵的理解。 三、写一写,灵活恰当运用 学习的最终目标是运用...

    六年级语文期末测试卷5.doc

    5. 词语填空及解释:如"竭泽而渔"、"余音绕梁"、"曲高和寡"等,要求学生理解和应用成语。 6. 诗词默写:涵盖多首古诗词,如白朴的《天净沙·秋》、鲁迅的《自嘲》等,检验学生的诗词积累。 7. 句子练习:包括句型...

    无领导小组讨论面试总结.pdf

    2. 语言运用:口齿清晰、语言流利,同时要注意语气、语调和音量的适中,适时的幽默可以缓解紧张气氛。 3. 手势:关注对方反应,用适当的手势表达关注和理解,但不宜过度。 4. 回答问题:把握重点,简明扼要,清晰...

    北京市第三十五中学高一语文期中试题.doc

    4. 成语运用:题目中涉及到成语的正确使用,如“曲高和寡”、“擢发难数”、“摧眉折腰”、“尽享天伦之乐”,这要求学生理解成语的含义并能恰当运用到句子中。 5. 文言文词汇解释:题目中包含文言文词汇的解释,如...

    广西壮族自治区蒙山中学2020学年高二语文第二次月考(无答案).doc

    9. 语言文字运用:试卷还涉及对句子语病的判断,这是对学生语言运用能力的考察,如选项中涉及的病句分析。 10. 诗词填空:试卷中要求填写与上下文语意连贯、音节和谐的诗句,这既测试了学生对古诗词的熟悉程度,也...

    湖南省永州市新田县第一中学高中语文 第四单元 11 包身工习题 新人教版必修1

    例如,题目中提到了“弄璋”、“说服”、“契据”等词的不同读音,以及成语“凤毛麟角”、“肆无忌惮”、“曲高和寡”的使用情境,这些都是高中语文教学中常见的考点。 2. **阅读理解与分析**:习题中还包含了对语...

    分宜2016年事业编招聘考试真题及答案解析版.docx

    7. 成语对应题,"班门弄斧"与"布鼓擂门"都是形容在行家面前卖弄本领,对应的成语应有相似含义,选项中"曲高和寡"与"雅俗共赏"不构成对应。 8. 统计数据分析,火山爆发频率与战争关联以及与19世纪火山活动性的对比,...

    六年级复习专题准确理解词语意思PPT教案学习.pptx

    3. **曲高和寡**:这里的“和”有附和的意思,因此“曲高和寡”表示歌曲的调子太高,能与之唱和的人很少,引申为高雅的艺术或观点往往只有少数人能理解和欣赏。 4. **独具匠心**:其中的“匠”指有手艺的人,“心”...

    天津市第一中学2021届高三下学期第四次月考语文试题 Word版含答案.docx

    2. 语言文字运用:试题中的成语和词语选择题,如“曲高和寡”、“束之高阁”、“盛行”、“风靡”、“毋庸置疑”、“无疑”、“一举两得”、“两全其美”,考察了考生对汉语词汇的理解和运用能力,以及在具体语境中...

    【备战2014】高考语文基础拔高训练37.doc

    1. 汉字读音:题目中提到了多组汉字的读音比较,如“供应/供词”的“供”(gōng/gòng)、“当场/安步当车”的“当”(dāng/dàng)、“曲突徙薪/曲高和寡”的“曲”(qū/qǔ)、“折耗/折中”的“折”(shé...

    教材全解语文版八年级语文下册第三单元检测题及答案解析精选.doc

    2. 语言知识 - 试题中的字音辨析题考察了多音字的不同读音,例如“角色/角逐”,“兴奋剂/兴冲冲”,“和颜悦色/曲高和寡”,“背包/背弃”,“开小差/差旅费”,“寡廉鲜耻/屡见不鲜”,“纤夫/纤维”,“人行道/...

    (人)版小学六年级语文积累运用升学考试总复习.doc

    通过以上知识点的学习和复习,学生不仅能增强语文基础知识,还能提升语言表达能力,为升学考试做好充分准备。同时,教师在指导时应注重启发式教学,鼓励学生在具体情境中应用所学,避免死记硬背,提高学习效果。

    世界非物质文化遗产昆曲PPT学习教案.pptx

    昆曲,作为世界非物质文化遗产,是中国古老而珍贵的戏曲艺术形式,起源于14、15世纪的苏州昆山,融合了歌唱、诵读、表演、舞蹈及武术,以中州官话为主要唱说语言,以鼓、板控制节奏,曲笛、三弦等为主要伴奏乐器。...

    云南省中考语文试卷及答案(word版).doc

    2. 词语辨析 - 错别字:例如“励精图治”的“励”不应该是“历”,“曲高和寡”的“和”不应该是“跟”。这要求学生能够识别和纠正常见的错别字,提高文字表达的准确性。 3. 成语应用:如“鬼斧神工”形容自然景观...

Global site tag (gtag.js) - Google Analytics