1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
# Reductions
In Tomo, reductions are a way to express the idea of folding or reducing a
collection of values down to a single value. Reductions use an infix operator
surrounded by parentheses, followed by a collection:
```tomo
nums := [10, 20, 30]
sum := (+) nums
>> sum
= 60
```
Reductions can be used as an alternative to generic functions like `sum()`,
`product()`, `any()`, and `all()` in Python, or higher-order functions like
`foldl` and `foldr` in functional programming:
```tomo
# Sum:
>> (+) [10, 20, 30]
= 60
# Product:
>> (*) [2, 3, 4]
= 24
# Any:
>> (or) [no, yes, no]
= yes
# All:
>> (and) [no, yes, no]
= no
```
## Minimum and Maximum
Reductions are _especially_ useful for finding the minimum or maximum values in
a collection using the `_min_` and `_max_` infix operators.
```tomo
# Get the maximum value:
>> (_max_) [10, 30, 20]
= 30
# Get the minimum value:
>> (_min_) [10, 30, 20]
= 10
```
The `_min_` and `_max_` operators also support field and method call suffixes,
which makes it very easy to compute the argmin/argmax (or keyed
minimum/maximum) of a collection. This is when you want to get the minimum or
maximum value _according to some feature_.
```tomo
# Get the longest text:
>> (_max_.length) ["z", "aaaaa", "mmm"]
= "aaaaa"
# Get the number with the biggest absolute value:
>> (_max_:abs()) [1, -2, 3, -4]
= -4
```
## Comprehensions
Reductions work not only with iterable values (arrays, sets, integers, etc.),
but also with comprehensions. You can use comprehensions to perform reductions
while filtering out values or while applying a transformation:
```tomo
# Sum the lengths of these texts:
>> (+) t.length for t in ["a", "bc", "def"]
= 6
# Sum the primes between 1-100:
>> (+) i for i in 100 if i:is_prime()
= 1060
```
## Empty Collection Behavior
If a collection has no members, the default behavior for a reduction is to
create a runtime error and halt the program with an informative error message.
If you instead want to provide a default fallback value, you can use `else:` to
give one:
```tomo
empty := [:Int]
>> (+) empty else: -1
= -1
>> (+) empty
# Error: empty iterable!
```
You can also provide your own call to `fail()` or `exit()` with a custom error
message, or a short-circuiting control flow statement (`return`, `stop`,
`skip`) like this:
```tomo
>> (_max_) things else: exit("No things!")
for nums in num_arrays:
product := (*) nums else: skip
do_thing(product)
func remove_best(things:[Thing]):
best := (_max_.score) things else: return
best:remove()
```
|