Mathematica笔记·四

Mathematica笔记·四

Mathematica第三原理:函数式操作

函数名作为表达式

在表达式如f[x]中,函数名称f 本身也是一个表达式,可以像对待其他任何表达式一样对待。能够把函数名称像其他类型的表达式一样对待,源于Wolfram语言的符号式本质,这使得全范围的函数式操作成为可能

普通的Wolfram语言函数(如:LogIntegrate)通常操作的式数字和代数表达式等数据,而代表函数式操作的Wolfram语言函数不仅可以对普通数据进行操作,还可以对函数本身进行操作,例如:函数式操作InverseFunction以Wolfram语言函数名称作为参数,来表示对函数的反函数

Wolfram语言中有很多种函数操作,有些代表数学运算,有些代表各种程序和算法,函数操作是从概念学习到实际应用Wolfram语言最有效的方法之一

重复应用函数

许多程序会涉及多次迭代的操作,NestNestList是执行此操作的强大结构

  • Nest[f,x,n] 将嵌套 n 次的函数 f 应用于 x
  • NestList[f,x,n] 生成列表 {x,f[x],f[f[x]],…},其中 f 的嵌套层深为 n

NestNestList可用于将函数应用固定的次数,通常,函数要应用到结果不在改变为止,这可以使用FixedPointFixedPointList来实现

  • FixedPoint[f,x] 重复应用函数f直到结果不在改变
  • FixedPointList[f,x] 生成列表 {x,f[x],f[f[x]],…},当元素不再改变时停止

同样的,我们也有对应用函数到结果无法满足所需条件为止的需求,可以使用NestWhileNestWhileList实现

  • NestWhile[f,x,test,m] 重复应用函数 f ,直到对 m(默认值是1,可选 All) 个最新结果应用 test 不再产生 True
  • NestWhile[f,x,test,m] 生成列表 {x,f[x],f[f[x]],…} ,直到对 m(默认值是1,可选 All) 个最新结果应用 test 不再产生 True

Nest 等操作接受一个单参数的函数 f ,然后重复应用,每一步都将上一步的结果用作 f 的新参数,将这一概念推广至两个参数的函数是很重要的,你可以再次重复应用该函数,但现在获得的每个结果仅提供所需的新参数之一,一种简便的方法是在每个步骤中从列表的连续元素中获取另一个参数

  • FoldList[f,x,{a,b,…}] 创建列表 {x,f[x,a],f[f[x,a],b],…}
  • Fold[f,x,{a,b,…}] 给出由 FoldList 生成的列表的最后一个元素

使用 FoldFoldList 可以在Wolfram语言中编写许多优雅高效的程序,在某些情况下,将 FoldFoldList 视为由第二个参数索引的一组函数的简单嵌套可能会有所帮助

对列表和其他表达式应用函数

在像 f[{a,b,c}] 这样的表达式中,列表是函数的参数,但是很多时候我们需要将函数直接应用于列表的元素,而不是整个列表,这在Wolfram语言中可以使用 Apply 等函数实现

  • Apply[f,expr,level] 将 f 应用于列表或表达式的指定层,level 的值是列表,当指定层是第一层时可以简写成 f@@@expr ,无指定层时默认是应用于表达式的顶层,可以简写成 f@@expr

一般来说,Apply 的作用是指定的函数替换表达式的表头

对表达式的部分项应用函数

如果有一个元素列表,将函数分别应用于列表中每个元素通常很重要,在Wolfram语言中这可以使用 Map 完成

  • Map[f,expr,level] 将 f 应用于表达式或列表的指定层中的每个元素,level 的值是列表,无指定层时或指定层是第一层时可以简写成 f/@expr
  • MapAll[f,expr] 将 f 应用于表达式的所有部分
  • MapAt[f,expr,{part1,part2,…}] 将f作用于expr中指定的项,其中指定项的索引(值是列表)是基于表达式的完全格式(可使用FullForm查看完全格式)
  • MapIndexed[f,expr,level] 将f作用域表达式指定层的元素(level未指定时默认作用于第一层),f的第二个变量则使用对应项的指标号
  • MapThread[f,{expr1,expr2,…},level] 将指定层中(level未指定时默认作用于所有)每个表达式中相同指标号的元素按顺序同时提供给f

注:Map是作用于指定,而MapAt是作用于指定MapAll则是作用于所有Map[f,expr] 实际上做的是将函数 f 封装在表达式 expr 的每个元素周围

注:可以设置选项 Heads->True将每个部分的标头及其元素用f 封装

值得一提的是,如果我们需要对表达式中所有相同的项应用MapAt,则可以使用Position函数,将所有相同的项的位置提取出来

实际上:Map是把一元函数作用于一个表达式的项上,而MapThread是将多元函数同时作用于多个表达式

Map等函数可以通过项的修改产生表达式,但有时不需要产生新的表达式,仅需要查看某些表达式,或者仅对表达式中某些项进行运算,一个典型的情况是当你所运用的函数具有某些“副作用”时,如进行赋值或产生输出时,我们则可以使用函数Scan

  • Scan[f,expr,level] 求出f作用于expr中第level层项时的函数值(未指定时作用于各项)

注:当指定层是Infinity时,Scan会从最底层开始访问表达式中所有项

纯函数

  • Function[{x1,x2,…},body] 纯函数中的xi可用任何变量替代
  • body& 自变量为##1#2#3等的纯函数

使用NestMap等函数运算时,总需要有一个确定函数名的函数去作用,纯函数可直接作用于变量,不需要函数名

Wolfram语言中的纯函数有多种形式,最理想的情况是定义一个目标函数,其作用于一个变量时给出函数值,当函数仅使用一次时,纯函数会比定义函数更加方便

熟悉形式逻辑和LISP编程语言的人会体会到Wolfram语言中的纯函数与λ表达式或无名函数相似,纯函数也类似于数学中的运算符

  • # 纯函数中的第一个变量
  • #n 纯函数中的第n个变量
  • ## 纯函数中的所有变量列
  • ##n 纯函数中从n个变量开始的变量列

如果你不想再次提到该函数的话,函数名是无关的,因此同样的,一个纯函数中的变量名也是无关的,Wolfram语言允许用户不使用纯函数变量的显示名字,另一方面,我们可以通过给出“位置数字”#n 来指明变量,在一个Wolfram语言纯函数中,#n表示所提供的第n个变量,#表示第一个变量

当使用纯函数的简化形式时,千万不要忘记&符号,否则Wolfram语言就无法理解和执行这一输入,在纯函数中使用&符号时,要注意&的优先级很低,必要时要用括号,这意味着,你可以不用括号输入形如#1+#2&的表达式,另一方面,如果你愿意对纯函数设置选项的话,必须使用括号,如:option->(fun&),在Wolfram语言中,纯函数可以选择任意数量的变量,##表示给定的任何变量,##n表示从第n项开始的所有变量

用函数产生列表

除了前文中提及的函数NestListFoldList,还有以下函数是常用的产生列表的函数

  • Array[f,{n1,n2,…,ni}] 产生一个i维列表,其列表沿第i维度方向的长度由ni决定,其列表的每个元素是函数f作用与其对应标号所得到的值
  • ComposeList[{f1,f2,…},x] 产生列表{x,f1[x],f2[f1[x]],…}

这些函数在与纯函数结合的时候,可以构造一些非常有效的Wolfram语言程序

用函数选择表达式的项

有时,我们需要根据内容来选取元素,而不是根据位置选取,于是我们有函数Select

  • Select[expr,f,n] 选择expr中的前n个(默认是全部)使函数f的值为True的项

在Mathematica笔记·二中的条件判断部分中介绍过一些Q函数,这些“判断词”常常用来作为Select的依据

具有非符号头部的表达式

大部分情况下,我们希望像f[x]这样的表达式的头部f是一个简单符号,但具有非符号头部的表达式也有一些重要应用,我们可以将任何表达式作用头部,但其必须放在括号内,将一个复杂的表达式作用于头部的情况在“纯函数”节中已提到,将Function作为表达式的头部时就定义了变量求值的函数

在Wolfram语言中有些类似于纯函数的结构,其用来表示数值函数等一些特殊的函数,在所有情况下,基本机制是给出能够包含所需要的函数完整信息的头部,如:

  • Function[vars,body][args] 纯函数
  • InterpolatingFunction[data][args] 由InterpolationNDSolve产生的近似数值函数
  • CompiledFunction[data][args] 由Compile产生的编译数值函数
  • LinearSolveFunction[data][vec] 由LinearSolve产生的矩阵解函数

复杂表达式作为头部的另一个重要应用是进行泛函运算

考虑一个求导运算的例子,在Wolfram语言中f'Derivative[1][f]表示,“函数操作符”Derivativep[1]作用于f后给出另一个函数f'

算子运算

在前面的Map中我们已经简单接触到了算子运算,即Heads->True

可以将表达式f[x]理解为算子f作用域表达式x上,f[g[x]]可以理解为算子fg复合作用于x上的结果

  • Composition[f,g,h,…] 复合函数f,g,h,…,形如:f[g[h[…]]],可使用符号@*表达
  • RightComposition[…,f,g,h] 复合函数f,g,h,…,形如:h[g[f[…]]],可使用符号/*表达
  • InverseFunction[f] f的反函数
  • Identity[expr] 恒等函数,即返回函数[expr]本身
  • Through[p[f,g,…][x1,x2,…,],q] 当qp相同时(或q缺省时),给出p[f[x1,x2,……],g[x1,x2,…],…]
  • Operate[p,f[x],n] 在f的第n层(缺省值是1)运用p

吐个槽:Identity的官方的应用示例过于奇怪,以至于我刚看见这个的时候蒙了半天

结构的操作

Wolfram语言有改变表达式结构的有效功能,这可以使我们实现合并,分配等数学特性,并对一些简洁和有效的程序提供基础,Wolfram语言函数Sort不仅排列列表元素,也对具有任意头部的表达式排序,用这种方式,我们可以对任意函数实现结合特性或对称性,下面将列举一些常用于排序的函数:

  • Sort[expr,pred] 用pred函数(缺省时使用默认方式)决定排列的方式
  • Ordering[expr,n,pred] 用pred函数(缺省时使用默认方式)给出排好序后前n个(缺省时返回所有)元素的索引
  • OrderedQ[expr] 当表达式expr的元素按照默认方式排序时返回True,否则返回False
  • Order[expr1,expr2] 按照默认排序方式时,expr1应该在expr2之前时返回1,否则返回-1

除了排序操作,我们还可以对嵌套结构进行一些处理,可以用来实现一些分配特性和线性性质

  • Flatten[expr,n,h] 压平头部是h(缺省时则对相同头部进行处理)的函数的嵌套结构,最多压平n层(缺省时压平所有层)
  • FlattenAt[expr,i] 压平表达式expr中的第i层结构
  • Distribute[expr,g,f,gp,fp] 头部是f时(缺省值是expr的头部),将f分配到函数g(缺省值是Plus),并分别用fpgp替代(缺省值是fg

一般的f分配到“和式Plus”时,时将如f[a+b]的表达式“展开”为f[a]+f[b],函数Expand对标准的代数乘法如Times进行类似的分配展开,但Distribute可以对任意运算进行类似的分配展开

吐个槽:我没看懂Distribute到底咋用,似乎是将表达式内所有的元素排列组合一遍?

Distribute相关的函数ThreadThread将函数并行的作用于一个列表或表达式的所有项

  • Thread[f[args],g] 将f线性作用与头部为g(缺省时作用于所有)的变量args

吐个槽:所以线性作用有没有更加好的解释方法?直接这么说我有点蒙,但是又看明白了

除此之外,我们还有一些结构上的常用操作

  • Outer[f,list1,list2] 广义外积,f作用于每个元素上
  • Inner[f,list1,list2,g] 广义内积,使用乘法算子f和加法算子g

Distribute一样,Outer给出元素所有可能的组合,而InnerThread一样仅给出表达式中相应位置元素的组合

序列

当我们需要对函数中的元素进行拼接时,Sequence用于实现这一功能

  • Sequence[e1,e2,…] 将元素自动拼接到函数

吐个槽:这个函数就是全自动头部变换函数,头部是啥变成啥,并带上压平属性

本文于2024.9.9编写,于2024.11.1完成

消息盒子

# 暂无消息 #

只显示最新10条未读和已读信息