| 
 | 
 
宗旨:除了上界变化的情况外,能不用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自行查看用法。  |   
 
 
 
 |