each (:E) / peach

语法

each(func, args...)

(把一个函数应用到指定参数中的每个元素。)

F :E X

(把一个函数应用到X中的每个元素。)

X <operator>:E Y

(把一个函数应用到X和Y中的每个元素,X和Y长度相同。)

参数

func 是一个函数。

args 是func的参数。

XY 可以是数据对、向量、矩阵或表。

详情

将指定函数应用到输入对象的每个元素。对于矩阵,把函数应用到每一列;对于表,把函数应用到每一行。

func(X) 和 func :E X 的区别是前者将X视作一个输入变量,而后者取遍X中的每一个参数。如果存在相应的向量函数,应该避免使用 ":E" ,因为在元素比较多的时候,元素的比对就会很慢。

peach 是并行计算版本的 each 高阶函数。对于执行时间较长的任务,peacheach 能节省大量的时间。但对于小任务,peach 可能执行时间要比each更长,因为并行函数调用的开销很大。

例子

假设需要计算3个员工的日薪,员工的工时存放在向量x=[9,6,8]中,员工的时薪在8小时以下是$10,在8小时以上是$20。考虑下面的 wage 函数:

$ x=[9,6,8]
$ def wage(x){if(x<=8) return 10*x; else return 20*x-80}
$ wage x;
The vector can't be converted to bool scalar.

wage(x) 不返回结果,因为x<=8,即 [9,6,8]<=8 返回了一个向量的条件值[0,1,1],而不是if 需要的标量。

可使用以下方案来解决这个问题:

$ each(wage, x);
[100,60,80]

$ wage :E x;
[100,60,80]

$ def wage2(x){return iif(x<=8, 10*x, 20*x-80)};
// iif 函数是一个逐元素的条件操作

$ wage2(x);
[100,60,80]

类似的,each 也可以用于有多个参数的函数:

$ def addeven(x,y){if (x%2==0) return x+y; else return 0}
$ x1=1 2 3
$ x2=4 5 6;
$ each(addeven, x1, x2);
[0,7,0]

each 所用数据可以是数据对:

$ t = table(1 2 3 as id, 4 5 6 as value, `IBM`MSFT`GOOG as name);
$ t;

id

value

name

1

4

IBM

2

5

MSFT

3

6

GOOG

$ each(max, t[`id`value]);
[3,6]

each 所用数据可以是矩阵:

$ m=1..12$4:3;
$ m;

col1

col2

col3

1

5

9

2

6

10

3

7

11

4

8

12

$ each(add{1 2 3 4}, m);
// add{1 2 3 4}是一个部分应用,each将向量[1, 2, 3, 4]与矩阵m的每列相加。

col1

col2

col3

2

6

10

4

8

12

6

10

14

8

12

16

$ x=1..6$2:3;
$ y=6..1$2:3;
$ x;

col1

col2

col3

1

3

5

2

4

6

$ y;

col1

col2

col3

6

4

2

5

3

1

$ each(**, x, y);
[16,24,16]
// 比如,24=3*4+4*3

下例中,我们在一个部分应用中使用了 call 函数,该部分应用将向量[1 2 3]作为参数,分别调用函数 sinlog

// 当 "functionName" 为空时,将动态地选择一个函数名字。
$ each(call{, 1..3},(sin,log));

sin

log

0.841471

0

0.909297

0.693147

0.14112

1.098612

性能提示

当元素数量很多时候,不推荐使用 :E (each) 高阶函数,可以使用更高效的向量解决方案。

$ x=rand(16, 1000000);
$ timer(10){each(wage, x)};
Time elapsed: 38164.9 ms

$ timer(10){iif(x<8,10*x,20*x-80)};
Time elapsed: 81.516 ms