Mathematica中的函数式编程-Map函数系列

最近接近接触到函数式编程的范式,深感它实在是非常有用。其最突出的特点是No side effect, no variables, no loops。在使用函数式编程的程序中,可以不包含一个任何一个循环和变量,同时无附带效应也使得出Bug的几率低了不少,调试起来也更加简单。这就使得编程的人更能集中精力思考如何解决问题而不是操心怎么用计算机实现。而Mathematica是老牌的科学计算软件,虽然冠以科学计算之名,但实际上它能处理的问题非常多,并不仅限于科学。同时它内建了丰富的函数库,在解决问题时也更灵活。个人认为完全可以取代python之类的编程语言。虽然python也有众多的函数库,但最大的问题是它们在风格上并不统一,参考文档也参差不齐,在学习上就有比较大的困难。

而Mathematica原生支持函数式编程,与丰富的库函数强强结合,简直是学习生活的不二之选。在Mathematica中的函数式编程风格有几个特点:

万物皆表达式

Mathematica中的所有语句都是表达式,那我们就可以把多个表达式整合到一个表达式中进行计算。所以用C++写出来的多个语句,在Mathematica中可以只用一个表达式完成。不过这样可能会带来可读性上的问题,因此可以适当地对表达式进行拆分,写成多个。

任何数据结构都是表

表在Mathematica中居于十分核心的地位,因为任何一种数据结构都可以等价为一个多维表,那么对表进行计算和操作也就是十分自然的了。Mathematica对表运算进行了特别的优化,因此建议多使用表。在对表的运算中,Map是一类核心的函数,事实上在任何一种函数式编程或类似的语言都能找到它的身影,诸如R语言的Apply,Matlab的Arrayfun这样的函数其在思想上和Map是一样的,就是将函数f应用于表中的每一个元素。

举个例子,我们想寻找一100以内的所有质数,用函数式编程的思想来做,我们就可以先生成一个1-100的整数表A,然后写一个函数f判断某一个数是否为质数,接着将这个函数Map到表上,从而得到一个长度为100的True和False组成的表B,接着从表A中提取表B对应位置为True的元素。

Map函数的正式语法为:

Map[f,expr] or f/@expr

其中的expr就是所要映射的表。同时如果有的表有多个层次,那么可以指定映射函数到哪一个层次的元素,即Map[f,expr,levelspec]。

对于刚开始的问题,我们用原生的PrimeQ作为判断函数f,那么就可以用PrimeQ/@Range[1,100],得到了表B,其中Range[1,100]表示生成从1到100的表。接着要判断哪些位置上的元素为True,此时需要用到Position函数,Position[PrimeQ/@Range[1,100],True]就表示表B中为True的元素的位置,然后用Extract[expr,list]提取expr中的由list指定的位置上的元素。

将以上程序写在一起就是

Extract[Range[1,100],Position[PrimeQ/@Range[1,100],True]]

就会得到{2,3,5...}这样一个100以内所有质数的表。

注意到上式中出现了两次Range,这就意味着程序会进行两次计算。因此我们可以先用一个变量存储s,写成如下的形式:

Extract[s=Range[1,100],Position[PrimeQ/@s,True]]

避免了二次计算。值得注意的是,与Haskell这样的函数式编程语言所使用的Lazy evaluation不同的是,在计算以上的语句时,Mathematica采取的策略是先计算能算的,因此它先计算的是s=Range[1,100],再计算内部的Position,所以给s赋值要写在Position外部。如果我们写成

Extract[s,Position[PrimeQ/@(s=Range[1,100]),True]]

这样是会出错的,因为s先于Position函数计算,此时它不知道s是什么。

与Map同系列的函数还有Mapthread、MapIndexed、MapAt、MapAll。就经验来看,Mapthread和MapIndexed使用得较其它两个更多。

MapIndexed顾名思义就是带目录的映射,Map[f,{a,b}]的作用是生成{f[a],f[b]},而MapIndexed[f,{a,b}]就是生成{f[a,{1}],f[b,{2}]}。不过值得注意的是在传递参数时,目录是作为一个单元素表{1}、{2}这样的形式进行传递的,因此在原函数中就要对第二个参数用First[]函数提取。

MapThread是用于带多个参数的函数进行映射。比如MapThread[f,{a1,a2,a3...},{b1,b2,b3...}]得到的就是{f[a1,b1],f[a2,b2],f[a3,b3]...}。事实上这两个函数可以都用Map实现。MapThread[f,{a1,a2,a3...},{b1,b2,b3...}]与Map[f,{{a1,b1},{a2,b2},{a3,b3}....}]的效果是差不多的。而MapAt可以指定将函数映射在某个或多个元素的位置上。MapAll可以将函数映射到表中的每个元素的每个子表达式上。MatAll[f,{a,{b}}]就可以实现{f[a],f[f[b]]}。,其中的元素{b}有两个层次 ,一是外部的表,二是内部的元素,MapAll就映射两次。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容