zoukankan      html  css  js  c++  java
  • 二、表操作


    二、表操作                                                               返回目录页
    1、引言

    2、表的创建与表的测量
    Range  Table  Length  Dimensions

    3、对表中元素的处理
    Position  Part  Take  Drop  Delete  First/Last/Rest  Select/Reverse/Sort
    RotateLeft/RotateRight  Flatten  Partiton  Transpose  Append/Prepend  Insert/ReplacePart

    4、对多个表的处理
    Join/Union  Complement/Intersection

    5、高阶函数
    Map/MapThread  Outer  Apply

    6、函数对表的重复作用
    Nest/NestList  Fold/FoldList

    7、字符串和字符
    StringLength...  Characters/StringJoin ToCharacterCode/FromCharacterCode

    ----------------------------------------------------

    1、引言
    上面可以看到,表操作函数有几十个。现在的MMA表操作函数,已经上千了。
    最常用的,也就那么几十个,并不多。
    我们熟悉MMA的函数,当从表操作函数开始。原因有几个:
    表是MMA中最常用的数据结构。
    表操作函数是最常用的MMA函数。
    我们学英语,要先非常熟悉最常用的220个单词(Sight Words),一样,学MMA,要先熟悉这几十个表操作函数。
    除此之外,还有个秘密原因,在本章的最后。如果急着想知道,就戳吧。

    MMA中表之强大,在于表中可以放任何表达式。
    List[2.4, dog, Sin, "read", {5, 3}, Pi, {}]
    {}是空表,empty list。

    List[2.4, dog, Sin, "read", {5, 3}, Pi, {}] // TreeForm
    MMA中表之强大,更在于,表可以是多维的。这个维数,可以从树形结构上去理解。


    ----------------------------------------------------
    2、表的创建与表的测量
    表的创建,只有两个函数。表的测量,也只有两个函数。

    表的创建的两个函数是:Range、Table

    Range[1, 10, 2]
    得:{1, 3, 5, 7, 9}
    三个参数分别是:最小、最大、步长。相当于在构建等差数列。

    Table[i, {i, 1, 10, 2}]
    结果与上面一样的。可以看到,{i, 1, 10, 2}中的含义:变量、最小、最大、步长。
    第一个参数i位置,可以放任何表达式。这个表达式可以与i有关,也可以与i无关。

    函数设计到这里,似乎差不多了。但MMA展示了灵活性:很多参数是可以省略的。
    A、如果步长为1,可以省略不写。
    B、如果起始最小值为1,可以省略不写。
    C、如果Table中的表达式与变量i无关,变量i可以省略不写。
    D、终止最大值,永远不可以省略。

    Range[5]
    Table[2i,{i,5}]
    Table["哈",{5}]

    有没有找到好玩的感觉?MMA这是在偷工减料啊。。不过,很多程序员很喜欢这种偷工减料,因为很好用,而且代码很简洁。

    Table[i + j, {j, 1, 4}, {i, 1, 2}]
    Table[i + j, {j, 1, 4}, {i, 1, 2}] // MatrixForm
    Table[i + j, {j, 1, 4}, {i, 1, 2}] // TableForm
    Table函数还支持多重循环。外层循环变量(j)要先写。也就是说,循环变量在Table参数中的顺序是有讲究的。
    Matrix是矩阵。这里一不小心,就构建出了一个矩阵。表作为最常用的数据,是因为可以构建很多东西。


    表的测量的两个函数是:Length、Dimensions
    很好理解,前者返回表的长度,后者返回表的维数。

    Length[{1, a, b}]  (*返回3,很好理解*)
    Dimensions[{1, a, b}]  (*返回{3},不好理解*)

    因为表啊,可以是一维,也可以是多维。在多维时,可以是规则的多维(比如矩阵),也可以是不规则的多维。所以使情况复杂了。
    我们来细看一下:

    lis = {{{1, 2}, {3, 4}, {5, 6}}, {{a, b}, {c, d}, {e, f}}};
    lis // TreeForm
    lis // Length
    lis // Dimensions
    lis
    Clear[lis];
    lis =.;
    lis

    lis只是给输入的表起了个名字,可以理解为别名。用完了之后,最好取消别名。程序中给出了两种取消的方法。
    给人家取了绰号,不能老叫啊。。老叫不礼貌。
    取消了之后,lis就是一个符号,不再是表的别名了。如果后面一不小心又用了lis,就不容易出错。

    观察树形结构,如果根节点是第0层,那么Length返回的是第1层的元素个数,这里是2。
    Dimensions 返回的是"规则"列表的维数列表,这里是:{2, 3, 2}
    意思是树形结构中,第0层节点均有2个分支、第1层节点均有3个分支、第2层节点均有2个分支。
    所谓“规则”列表的意思是,每个节点上的分支数是要一样的。
    那如果不一样会咋样呢?

    Dimensions[{{a, b, c}, {d, e}, {f}}]
    得:{3}
    根节点下,有3个分支,OK,得3。然后第1层节点下分支数不同了,Dimensions就停止工作(输出),歇菜。

    {{a, b, c}, {d, e}, {f}} // TreeForm
    TreeForm很好用啊。。树形结构很好用。。



    ----------------------------------------------------
    3、对表中元素的处理
    处理?无非取得一些、添加一些、替换一些。还有一些是你想不到的,我们一一来举例说明。

    ---------------------------
    Position[{a,7,a,2,4,4},a]
    得:{{1}, {3}}
    列表中的元素位置,是从1开始的,不是从0开始。
    为啥不返回:{{1,3}} ?

    请看:
    Position[{{1,f,3},{4,5,f}},f]
    得:{{1, 2}, {2, 3}}。f在第1行的第2列,和在第2行的第3列。
    {1,2}这种形式,已经有人用过了。。
    还有一个要注意一下:列表函数的返回值,经常以列表形式出现。因为列表函数经常嵌套使用,表中有表。。

    ---------------------------
    Part[{1,2,f,4},3]
    {1,2,f,4}[[3]]
    抽取表中的第3个元素,这两种方式抽取,结果是一样的。
    也就是说[[]]只是表面,在MMA内部,还是Part...还记得吧?MMA内部结构,都是函数(表达式)。

    Part还有两个功能:
    Part[{{1, f, 3}, {4, 5, f}}, 1, 2]
    得:f
    取得数组中的第1行、第2列元素。
    在MMA中,数组经常指比较规则的表(用树形结构看,分支数相同),与C、Pascal中的数组概念不同的。
    这个程序看起来,有点不太明显,用下面的是一样效果:
    {{1, f, 3}, {4, 5, f}}[[1, 2]]
    这样看起来,要好理解很多。

    Part[{{1, f, 3}, {4, 5, f}}, {1, 2}]
    {{1, f, 3}, {4, 5, f}}[[{1, 2}]]
    这两句是等效的,猜猜看,得到什么结果?

    既然如此,来个更复杂的:
    {{1, f, 3}, {4, 5, f}}[[{1, 2}]][[{1, 2}]][[{1, 2}]][[1,2]]
    这就是传说中的语法糖。
    用好了,代码可阅读性急剧提高。
    用差了,代码就不知所云了,变成恶搞了(比如上面这个)。

    ---------------------------
    Take,可以前取、后取、连续取

    Take[{1,2,3},1]
    Take[{1,2,3},-1]
    Take[{1,2,3},{1,2}]
    Take[{1,2,3},{-3,-1}]
    Take[{1,2,3},{-3,2}]

    Drop,可以前删、后删、连续删
    Drop[{1,2,3},1]
    Drop[{1,2,3},-1]
    Drop[{1,2,3},{1,2}]
    Drop[{1,2,3},{-3,-1}]
    Drop[{1,2,3},{-3,2}]

    ---------------------------
    Delete,删除指定位置上的元素。

    Delete[{1, 2, 3, 4, 5}, {{2}, {3}}]
    得:{1, 4, 5}

    Delete[{{1, f, 3}, {4, 5, f}},{1, 2}]
    得:{{1, 3}, {4, 5, f}}

    Delete要删除的,都必须特别指定位置。如果想连续删,用Drop。

    ---------------------------
    First/Last/Rest,不言自明:

    First[{1,2,3}]
    Last[{1,2,3}]
    Rest[{1,2,3}]

    注意两点:First和Last返回的不是表,是表中的元素。
    而Rest返回的是表。

    ---------------------------
    Select/Reverse/Sort,又是不言自明的。
    Select根据特定条件抽取表中元素。特定条件就是谓词,返回布林值,即不是真就是假。
    Reverse是反转表。
    Sort是排序。

    Select[{1,2,3,4},EvenQ]
    Reverse[{1,2,3,4}]
    Sort[{2,1,4,3}]
    与EvenQ类似的有很多,可以顾名思义:IntegerQ  OddQ  TrueQ  PrimeQ ...
    Sort默认是排出升序。但也可以指定为降序:
    Sort[{4, 1, 3, 2, 2}, Greater]
    在Greater位置,也可以放上排序函数。

    ---------------------------
    RotateLeft/RotateRight
    这两个函数,可以把表看成是可以转动的轮子。
    可以指定转动步长。

    RotateLeft[{1,2,3,4},1]
    RotateRight[{1,2,3,4},2]

    ---------------------------
    Flatten
    可以把嵌套表,展平(flatten,或者翻译成压平更容易理解?)到各种不同的层次。
    说到层次,又要用树形结构。

    lis = {{{1, 2}, {3, 4}, {5, 6}}, {{a, b}, {c, d}, {e, f}}};
    lis // TreeForm
    Flatten[lis, 1]
    % // TreeForm
    Flatten[lis, 2]
    % // TreeForm
    lis =.;

    观察输出结果,很容易理解。感性理解,就是一层一层剥皮,即把{}一层一层从外到内剥离。
    那么不指定层次会如何?指定层次越大了会如何?可以玩一下:

    lis = {{{1, 2}, {3, 4}, {5, 6}}, {{a, b}, {c, d}, {e, f}}};
    lis // TreeForm
    Flatten[lis]
    % // TreeForm
    Flatten[lis, 200]
    % // TreeForm
    lis =.;
    答案揭晓:不指定层次,是一剥到底。层数过大时,也一样是一剥到底。

    ---------------------------
    Partition
    功能十分强大而复杂,这里只介绍常用的两个功能。

    Partition[list,n]
    将列表 list 分割成不重叠的具有长度 n 的子列表
    Partition[Range[10],2]

    Partition[list,n,d]
    生成偏移量为 d 的子列表
    Partition[Range[10],2,5]

    ---------------------------
    Transpose
    转置表中的前两层。

    lis = {{a, b, c, d}, {1, 2, 3, 4}};
    lis // Transpose
    lis =.;
    得:{{a, 1}, {b, 2}, {c, 3}, {d, 4}}
    从表的角度看,是内部穿线一样。

    从二维数组或者矩阵的角度看,如同行列转换:
    lis = {{a, b, c, d}, {1, 2, 3, 4}};
    lis // MatrixForm
    Transpose[lis] // MatrixForm
    lis // TableForm
    Transpose[lis] // TableForm
    lis =.;

    ---------------------------
    Append/Prepend
    添加元素到表的前面、后面。

    Append[{1,2,3},4]
    Prepend[{1,2,3},4]

    ---------------------------
    Insert/ReplacePart

    Insert[list,elem,n]
    在 list 中的位置 n 上插入 elem。 如果 n 为负,位置从结尾计算。

    Insert[{1, 2, 3}, a, 2]
    Insert[{1, 2, 3}, b, -2]
    可以观察到,当n为正数时,插入到指定位置前面。当n为负数时,插入到指定位置的后面。

    ReplacePart[expr,i->new]
    产生一个表达式,其中用 new 替换 expr 的第 i[Null] 个元素.

    ReplacePart[{1, 2, 3}, a, 2]
    第2个位置的元素(2),被a替换掉了。
    这是MMA较老版本(比如2.0版)的写法,现在也是支持的。较新版本(比如9.0版本)一般写成这样:
    ReplacePart[{1, 2, 3}, 2 -> a]
    效果是一样的。但新版本的写法可以扩充:
    ReplacePart[{1, 2, 3}, {1 -> a, 3 -> c}]
    这里第一次碰到转换符号:->,以后还会详细说的。


    ----------------------------------------------------
    4、对多个表的处理

    Join / Union
    Join[{1,2},{3,4},{5,6}]
    Join将数个表连接起来。另外,Join还可以指定层数连接,这里不举例了。

    Union[{6,6},{3,4},{1,1}]
    Union连接数个表之后,还把重复元素去掉,然后再排序。这有点集合操作的味道了,取并集。
    因为Union有去重排序功能,当然也可以对一个表操作。但Union没有指定层数的功能。


    Complement / Intersection

    Complement求第一个列表中不出现在后面任何一个列表中的元素
    Complement[{a, b, c, d, e}, {a, b, c}]
    得:{d, e}
    判断元素属于集合A、而不属于集合B。
    (*
    这里似乎缺少一个属于的判断函数。
    可以使用Position函数与Length函数来做属于函数。

    ——想多了,可以F1这两个函数:Element  MemberQ
    *)
    MMA似乎还缺少一个取补集的函数。且慢,如果把Complement函数的两个参数,看作表1与表2,
    然后呢,把表1看作是全集,把表2看作是子集,那么Complement返回的就是补集!


    继续来看Intersection函数。
    Intersection[{1, 2, 1, 3}, {2, 1, 4}, {4, 1, 2, 3}]
    得:{1, 2}
    求交集。因为类似于集合运算,所以有去重排序功能。

    如果用语法糖,直接写成这样,会更清楚:
    {1, 2, 1, 3} [Intersection] {2, 1, 4} [Intersection] {4, 1, 2, 3}


    ----------------------------------------------------
    5、高阶函数
    有一些内置函数,把其他函数作为自己的参数,我们称之为:
    高阶函数(higher order function)。
    其实就是函数嵌套啦。

    ---------------------------
    Map
    这个函数可以将某一函数,作用于一个表的每一元素上。

    Map[f, {3, 5}]
    Map[Reverse, {{a, b}, {c, d}}]
    Map[Sort, {{2, 6, 3}, {2, 4, 1}, {2, 3, 1, 4, 6}}]
    MMA经常搞批发。这里显现出来了。

    ---------------------------
    MapThread
    多表元素,被分别组配到一起作为函数的参数。看例子:

    MapThread[g, {{a, b, c}, {x, y, z}}]
    MapThread[Power, {{2, 6, 3}, {5, 1, 2}}]
    MapThread[List, {{5, 3, 2}, {6, 4, 9}}]
    Transpose[ {{5, 3, 2}, {6, 4, 9}}]
    最后两行的功能是一样的,Transpose的功能前面说过。
    但是呢,Transpose只能有类似于行列转换的功能,而MapThread功能就强多了。
    另外,MapThread还有指定层数的功能。

    ---------------------------
    Outer
    将把一个函数作用于几个表的元素的一切组合上。这是数学上外积(outer product)概念的一般化。

    Outer[f, {a, b}, {2, 3, 4}]
    Outer[List, {a, b}, {2, 3, 4}]

    ---------------------------
    Apply
    替换函数头。

    Apply[f, List[1, 2]]
    Apply[Plus, List[1, 2]]

    ---------------------------
    这里多作点说明。
    因为Map与Apply非常常用,所以有必要多了解一些。

    Map[f, {3, 5}]
    f /@ {3, 5}

    Apply[f, List[1, 2]]
    f @@ List[1, 2]

    前后两句的功能是完全一样的,
    Map的语法糖是:
    /@  分别作用于
    Apply的语法糖是:
    @@  仅替换函数头

    ---------------------------
    Map与Apply,均可以指定层数。

    Map[f,expr] 或 f/@expr
    将 f 应用到 expr 中第一层的每个元素.

    lis = {{{1, 2}, {3, 4}, {5, 6}}, {{a, b}, {c, d}, {e, f}}};
    lis // TreeForm
    Map[f, lis] (*作用于第1层*)
    Map[f, lis, {2}] (*作用于第2层*)
    Map[f, lis, 2] (*作用于第1、2层*)
    lis =.;

    Apply[f,expr]
    或 f@@expr 用 f 替换 expr 的头部.
    lis = {{{1, 2}, {3, 4}, {5, 6}}, {{a, b}, {c, d}, {e, f}}};
    lis // TreeForm
    Apply[f, lis] (*作用于第0层*)
    Apply[f, lis, {2}] (*作用于第2层*)
    Apply[f, lis, 2] (*作用于第1、2层*)
    lis =.;

    ---------------------------
    附加说明函数的属性。

    MapThread[Plus,{{1,2,3},{1,2,3}}]
    得:{2, 4, 6}

    直接写:
    {1, 2, 3} + {1, 2, 3}
    结果一样。

    既可以被自动地转到表参数的各个元素上,又可以被自动地穿线于表参数的对应元素上的那些函数,我们称之为具有可作用于表的元素的(Listable)属性,即具有Listable属性。许多MMA内置函数都具有这一属性。
    查看函数的属性,用Attributes函数:

    Attributes[Log]
    Log[{2, 3, 4}, {4, 9, 16}]
    因为Log函数有Listable属性,所以有这种“穿线”功能。

    ---------------------------
    Attributes[Plus]
    得:{Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected}
    Flat, 可结合的,即服从结合律。
    Listable, 前面已经说明,有“穿线”功能。
    NumericFunction, 顾名思义,用于数值计算的函数。
    OneIdentity, 函数对同一参数的重复作用是无效的,Plus[Plus[a + b]] // FullForm
    Orderless, 无顺序的,即服从交换律。a+b与b+a一样的。
    Protected, 受保护的。用户被禁止以任何显著的方法来修改这个符号。有点其他语言中保留字的味道。


    ----------------------------------------------------
    6、函数对表的重复作用
    这些函数,对做迭代非常方便。

    ---------------------------
    Nest / NestList

    Nest[f, x, 5]
    得:f[f[f[f[f[x]]]]]
    函数作用于参数,返回值作为参数,再被函数。。作用了5次。

    NestList[f, x, 5]
    得:{x, f[x], f[f[x]], f[f[f[x]]], f[f[f[f[x]]]], f[f[f[f[f[x]]]]]}
    整个作用过程,给出了表。这样全过程就很清楚了。

    ff[x_] := x + 1;
    NestList[ff, 0, 20]

    ---------------------------
    Fold / FoldList
    在每一次迭代中,即函数返回值作为参数时,还另加表中的一个元素作为第二个参数。

    Fold[f, 0, {a, b, c, d}]
    得:f[f[f[f[0, a], b], c], d]

    FoldList[f, 0, {a, b, c, d}]
    将整个过程中,每个步骤所得,均取出来,作为表元素。

    FoldList[Plus, 0, {a, b, c, d}]
    FoldList[Plus, 0, {3, 5, 2, 4}]

    FoldList[Plus, 0, Range[100]]
    这个迭代过程很低效,只是为了说明Fold的迭代过程。1+2+3+...+100,最后得5050

    ---------------------------
    Inner
    被看成是数学中点乘运算(Dot函数)的推广。

    Dot (.)
    a.b.c 或 Dot[a,b,c]
    给出向量、矩阵和张量的乘积,即点乘运算。
    {a, b, c} . {x, y, z}

    Inner[f, {a, b, c}, {d, e, f}, g]
    得:g[f[a, d], f[b, e], f[c, f]]
    f起到“穿线”组合参数的功能。而g起到最后的结合功能。

    Inner[Times, {a, b, c}, {d, e, f}, Plus]
    得:a d + b e + c f

    Inner[List, {a, b, c}, {d, e, f}, Plus]
    先是List起作用,产生三组表。然后是Plus起作用,用了“穿线”加法,最后得:
    {a + b + c, d + e + f}


    ----------------------------------------------------
    7、字符串和字符
    字符串,在MMA中,是atom,最基本的数据类型。类似于一个值,不,就是一个值。
    我们说,在MMA中,一切都是表达式。除了函数,就是atom。
    字符串不是函数、更不是字符数组,是atom。

    1 // FullForm
    "test" // FullForm
    "test" // InputForm
    Length["mathematica"]

    ---------------------------
    StringLength...

    字符串很特殊啊、不是这个不是那个,更不是表。所以表操作函数,不能直接用于字符串。
    坏消息啊。
    不过,好消息是:一般的表操作函数,前面加上String,就可以用了:

    StringLength["Mathematica"]
    StringReverse["abcdefg"]
    StringTake["abcde", 3]
    StringDrop["ABCDE", -1]
    StringPosition["abcde", "bc"]
    ...
    可以看到,从原理上,把字符串作为字符列表来处理了。。
    从学习上来说,我们不必从头到尾再学习一遍串操作函数了,因为与表操作函数太像了。。
    一眨眼间,大部分串操作函数就理解了。
    接下来,来几个针对串操作的固定有函数。

    ---------------------------
    ToCharacterCode/ FromCharacterCode

    ToCharacterCode["mathematica"]
    % - 32
    FromCharacterCode[%]
    第一句,转化字符为ASC码。
    第二句,上句输出表中每个元素减去32。
    第三句,上句输出表中每个元素,转化为字符。
    可以看到,小写都变成大写了。

    因为字符与ASC码之间,可以轻松转化,那字符操作,就没有问题了。


    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    秘密就在于:
    因为MMA中任何事物,都具有表达式这一共同结构,所以在表操作中学到的大部分内置函数,也可以用来对任何表达式(atom除外)操作。
    清楚了吧?很多表操作函数,不是表专用的,其他表达式上都可以用。
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    虽然这章有点长,但我们可以说,最常用的MMA函数,我们都掌握了。
    MMA学习曲线上的重头戏,就这么愉快地上演完毕。

    ++++++++++++++++++++++++++++++++

    扩展阅读:木有。如果有时间,把常用表操作函数拼写几遍,熟练一下函数功能。

    这里有个mathematica 常用命令大全,按函数功能进行划分,有时候可以查阅。

     

                         Top




  • 相关阅读:
    windows常用命令行总结
    express安装
    MySQL去除外键关联关系
    c#实体转化
    C#之Clone
    mysql 将null转代为0(转)
    Mysql显示行号
    mysql存储过程游标加计划任务事件调度器
    mysql临时表
    Git学习笔记
  • 原文地址:https://www.cnblogs.com/xin-le/p/5992392.html
Copyright © 2011-2022 走看看