|
宗旨:除了上界变化的情况外,能不用for就不用for,for mma is a Functional Programming Language
举个上界变化的例子:
x = 11;
For[i = 1, i <= x, ++i; Print, x -= 2; Print[x]]
x如果
这貌似是很经典的一个问题,虽然没什么说的,但是我还是另开了一个标题。
If[(*条件*),(*条件符合的话……*),(*否则*)] 如果你说我否则里面并没有啥要否则的,那么
a = 1
If[a < 0, a, Null]
映射
Map[f, {a, b, c, d, e}](*把函数f映射到一个数表中所有元素上*)循环,迭代,遍历条件
其实说到循环,函数可就太多了。但是不知道是甚么原因,“For”经常会被视作循环的同义词。其实与我们大家都很熟悉的语言——比如python——不同的是,在mma中,用For表示的循环通常都是低效且有很大程度上的繁杂性的——而且还很慢。
因为提升mma速度的方法之一就是把所有想计算的东西扔到kernel里面去——不要放到外面,正如for干的一样,他还会把数组拆散去挨个分析。
不说别的,先带大家看一下For最基本的用法。
In[13]:= list = {}; For[i = 2, i <= 10, i = i + 2,
AppendTo[list, Exp + i^2]]; list
Out[13]= {4 + E^2, 16 + E^4, 36 + E^6, 64 + E^8, 100 + E^10} 这里面就是for最基本的功能——生成一个列表。
构造表
list = {}; For[z = 7, z <= 8, z = z + 1/2,
For[y = 5, y <= 6, y = y + 2/10,
For[x = 2, x <= 3, x = x + 1/10, AppendTo[list, {x, y, z}]]]];
Graphics3D[Point
]
(*另外一种写法,Graphics3D@Point@list*) 举个例子,上面是在说[创建一个list,然后在z=7时开始,判断z=7,小于8,好下一个y=5开始,y小于6,下一个x=2开始,小于三,于是这三个xyz的赋值成功地被添加到了最后的表中。]
当然,mma中还有其他类似函数:Array和Table。
a = Array[# &, 100];
Cases[_?EvenQ][a];
F[x_] := E^x + x^2;
F /@ %%
(*或者*)
Table[F[x], {x, 2, 100, 2}] Array正如其名,数组,指构造一组数,用的是匿名函数。当然现在看来,这两个函数还没有什么用。
插一句,如何理解Table这个函数?
Table[If[PrimeQ@i, i, Nothing], {i, 1000}]
我来解释一下:
Table把后面的那个参数也就是数表中的各个元素往前面扔,前面的一参数是个函数,这个元素经过了前面的函数的加工后被append到最终的生成表中。 同样的目的也可以用if实现,不过没必要展开讲;用Range直接实现的话是这样子:
Exp@# + #^2 &@Range[2, 100, 2]
Exp@# + #^2 & // Range[2, 100, 2] 这两个乍一看还真差不多,不过一定要注意,后者的等效写法为Range[2, 100, 2]@Exp@# + #^2 &,并无意义!@是将前面的函数——这里面是隐函数,applyto后面的数组中!不能把数组作用在函数上(?听起来也怪怪的)
循环
$$
\sum i=\sum _{i=1}^{n} \sin (i)
$$
一个小技巧。在mma中打公式为了防止粘贴到别处显乱,可以先Ctrl+Shift+I。
Sum[Sin, {i, 1, 10^6}] 作为一句废话,这当然是最简单的手段。
但是与此同时不要忘了——咱们的题目可是《循环》啊。所以这个求和可以用循环表示:
For[i = 1(*从1开始*), (*直到*)i <= 10^5, (*每运行一次*)i++(*就是i+=1的简写,i每运行一次都加1*), sum += Sin[N@i](*每运行一次sum加Sin[N@i],最后sum的赋值便是那个求和的结果*)] 很慢,不是嘛?你可以试试用Module封装完了(后面会讲)、AbsoluteTiming之后看看时间,比如说:
Do[a[n] =
AbsoluteTiming[
Module[{sum = 0, i}, For[i = 1, i <= 10^5, i++, sum += Sin[N@i]];
sum]], {n, 1, 10}];
Do[b[n] = a[n][[1]], {n, 1, 10}];
Table[b[n], {n, 1, 10}];
Mean@% 这时候你就会意识到Do的最基本用法——Do[Option,n次],通常来讲Do均比For快。
如果想更快一点,可以:
Tr@Sin[N@Range[10^6]] 这里面tr是广义迹,也就是将后面的一串sini来看作一组 向量在各维的投影,求广义迹便是求元素和,也即向量化运算——这种运算方式的优越性在后面也会有提到。当然了,Add肯定比tr快,毕竟人家是专门干这个的。
Module[{}, Add@{1, 2, 31, 3^12341234}] // AbsoluteTiming直到……
上面刚说完Do这个函数,那么有的人就会对这个函数的普适性提出质疑了:
Do函数,仅仅执行不能判断。但是For就很好。比如一段循环,满足精度就停止。这时用Do,需要添加别的判断语句了 那么这种情况自然Do麻烦了,但是不代表没有其他的函数能怼上。函数式编程,讲究的就是个各有所长。
迭代
接下来说说迭代。
举个例子:
x = 0.5;
For[i = 1, i <= 10^6, i++,
x = 3.5 x (1 - x);{}];
x 想要快一点点就别都加For,试试Nest:
Nest[3.5 # (1 - #) &, 0.5, 10^6] nest也不难理解,就是声明一个纯函数再把0.5带进去,计算出结果,再把该结果带进去,出下一个结果,像这样重复10^6次——不过这样就会更快了。
遍历条件、筛选
再说说遍历列表求偶数个数。
前面咱们已经说过了,规则可以用f[x_?EvenQ] := 3x;(*EvenQ是规则,判断是否为偶数*)类似的“?+条件”来表明。那么下面也一样。
(*前设list为一10^6长度的随机数表*)
num = 0;
For[i = 1, i <= 10^6, i++,
If[EvenQ@list[], num++]
];
num 很好理解。那么比For更正宗的,是Scan:
num = 0;
Scan[If[EvenQ@#, num++] &, list];
num 或者:
Count[list, _?EvenQ] // AbsoluteTiming
(*直接用Count数出list中偶数的个数*)
Count[EvenQ /@ list, True] // AbsoluteTiming
(*用Map(/@)把判断偶数的规则映射到list上,然后用Count数出结果中True的个数*)
Select[list, EvenQ] // Length // AbsoluteTiming
(*选取list中的所有偶数,然后求结果列表长度*)
Count[EvenQ@list, True] // AbsoluteTiming
(*利用EvenQ的Listable属性直接判断list的每个数是否偶数,然后数出结果中True的个数*)
(*原网页链接:https://www.zhihu.com/question/34573590/answer/59844631 一般看哪个快主要看参数的数量、用途的专一性。不过作为一个看起来不太专一的例子,下面的代码其实是最快的:
Sum[Boole[EvenQ@i], {i, list}]
(*i取1、2、3,求布尔值的和——当i为偶数时布尔值为1*) 还有下面的Table通法:
Table[If[EvenQ@i, i, Nothing], list] // Length 前面已经提到过了,不再赘述。
在一部分例子中,筛选的本质是模式匹配,我就在模式匹配那一章中再做展开吧。
代码整理工具——Module
Module其实不难理解,就是把程序划分成一块一块以增加可读性、免去重复管理变量赋值的一种快捷手段。Module[{x,y},expr]的意思是把xy看作局部变量,非全局变量,出了这块地方xy就恢复成原来的未赋值状态了;Module[{x=0},expr]的意思是给出局部的初值。一般在计算过程所需时间时都需要用AbsoluteTiming搭配module使用。
Do[a[n] =
AbsoluteTiming[
Module[{sum = 0, i}, For[i = 1, i <= 10^5, i++, sum += Sin[N@i]];
sum]], {n, 1, 10}];
Do[b[n] = a[n][[1]], {n, 1, 10}];
Table[b[n], {n, 1, 10}];
Mean@% 如果你非得想要把每次的被包起来的结果输出的时候,可以用print函数,F1自行查看用法。 |
|