each (:E) / peach

Syntax

each(func, args…)
(apply a function to each element of the specified parameters)

or

func :E X
(apply a function to each element of X)

or

X <operator>:E Y
(apply an operator to each element of X and Y)

Arguments

func is a function.

args … are the required parameters of func.

X / Y can be scalar/vector/matrix/table.

Details

Apply a function to each element of args…. For matrices, apply a function to each column; for tables, apply a function to each row.

The difference between func(X) and func :E X is that the former treats X as the one input variable while the later takes each element in X as an input variable. We should avoid using “:E” if we can find corresponding vector functions since element-wise operations are very slow with a large number of elements.

peach is the parallel computing version of template function each . For tasks that take a long time to finish, peach can save a significant amount of time over each. For light tasks, however, peach may take longer than each as the overhead of parallel function call is not trivial.

Examples

Suppose we need to calculate the daily compensation for 3 workers. Their working hours are stored in vector x=[9,6,8]. Their hourly rate is \(10 under 8 hours and \)20 beyond 8 hours. Consider the following function 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) does not return a result, as x<=8, i.e., [9,6,8]<=8 returns a vector of conditions [0,1,1], not a scalar condition that is required by if.

In contrast, consider the following solutions:

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

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

$ def wage2(x){return iif(x<=8, 10*x, 20*x-80)};
// the iif function is an element-wise conditional operation

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

Similarly, each can also be applied to a function with more than one parameter.

$ 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 with a tuple:

$ 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 with matrices:

$ 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);
// note add{1 2 3 4} is a partial application, which adds [1, 2, 3, 4] to each of the 3 columns

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

In the example below, we use the function call in a partial application that applies each of [sin, log] to vector 1..3

// when "functionName" is empty, it will be filled with function names dynamically.
$ each(call{, 1..3},(sin,log));

sin

log

0.841471

0

0.909297

0.693147

0.14112

1.098612

Performance Note

Template :E (each) is not recommended when there is a large number of elements. In those scenarios we should look for more efficient vector solutions.

$ 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