Python and Viv · Lists and growable arrays
Slices, copies and aliases
What to notice
- A slice is a copy in Viv and a view in NumPy. This is the one place where the same code computes something different; the program shows it on
d. - Plain assignment shares the array in both: after
c = a, a write throughcis seen througha. a[-1]anda[-3:]count from the end in both. An index before the start stops the run in Viv, as it raises in NumPy; a slice bound outside the array stops it too, where NumPy clamps.np.arange(10.0)has no builtin twin; the Viv side fillszeros(N)in a loop.np.argmaxandnp.cumsumare loops in Viv.a[5:8] = 0.0writes a number into every element of a slice in both.- NumPy prints arrays as
[2. 3. 4.]; the Python side printstolist()and the Viv side printsfmt(a), which both write[2.0, 3.0, 4.0].
Python slicing.py · 41 lines
Viv slicing.viv · 58 lines
py2viv refuses slicing.py
1 imports
4import numpy as np
2# nothing to import: arrays are part of the language
py2viv refuses this program:
slicing.py:10:60: not numeric Python: a slice with the step `STEP`
print(a[2:5].tolist(), a[:3].tolist(), a[7:].tolist(), a[::STEP].tolist())
^^^^
Viv's slices take a literal step of 1 or more; a negative step walks backward, which they do not. Write the loop over the indices.
slicing.py:18:1: not numeric Python: a write through `part`, which is a slice of another array
part[0] = 99.0
^^^^^^^
Rows and slices are copies in Viv; write into the original array: `A[i, j] = ...`.
2 constructs outside the numeric subset (tools/py2viv/SUBSET.md) 2 slices of an array
7N = 10
8STEP = 3
9a = np.arange(float(N))
10print(a[2:5].tolist(), a[:3].tolist(), a[7:].tolist(), a[::STEP].tolist())
5N = 10
6STEP = 3
7a = zeros(N)
8for i to N {
9 a[i] = i
10}
11print(fmt(a[2:5]), fmt(a[:3]), fmt(a[7:]), fmt(a[::STEP]))
(refused)
3 the last elements
13print(a[-1], a[-3:].tolist())
14print(fmt(a[-1]), fmt(a[-3:]))
(refused)
4 a slice is a view in NumPy and a copy in Viv
16d = np.zeros(4)
17part = d[1:3]
18part[0] = 99.0
19print("d[1] after a write through the slice:", d[1])
17D = 4
18d = zeros(D)
19part = d[1:3]
20part[0] = 99.0
21print("d[1] after a write through the slice:", fmt(d[1]))
(refused)
5 assignment shares the array
22c = a
23c[0] = -1.0
24print(a[0])
24c = a
25c[0] = -1.0
26print(fmt(a[0]))
(refused)
6 write into a slice
27a[5:8] = 0.0
28print(a.tolist())
29a[5:8] = 0.0
30print(fmt(a))
(refused)
7 where is the largest value
31x = np.array([3.0, 8.5, -2.0, 8.5, 1.0])
32k = int(np.argmax(x))
33print(k, x[k])
33x = [3.0, 8.5, -2.0, 8.5, 1.0]
34k = 0
35for i to len(x) {
36 if x[i] > x[k] {
37 k = i
38 }
39}
40print(k, fmt(x[k]))
(refused)
8 running sums
36running = np.cumsum(x)
37print(running.tolist())
43running = zeros(len(x))
44total = 0.0
45for i to len(x) {
46 total += x[i]
47 running[i] = total
48}
49print(fmt(running))
(refused)
9 checks
40assert a[0] == -1.0
41assert running[-1] == np.sum(x)
52test "slices" {
53 assert_true(a[0] == -1.0)
54 assert_true(running[-1] == sum(x))
55}
(refused)
What they print
differs — One line differs and is left out of the comparison. A NumPy slice is a view, so writing into it changes the array it came from (d[1] becomes 99.0); a Viv slice is a copy, so d[1] stays 0.0.
Dimmed lines are left out of the comparison. Each Viv test block prints one PASS line; the Python checks the same facts with assert, which prints nothing. The remaining lines match.
$ python3 slicing.py
[2.0, 3.0, 4.0] [0.0, 1.0, 2.0] [7.0, 8.0, 9.0] [0.0, 3.0, 6.0, 9.0]9.0 [7.0, 8.0, 9.0]d[1] after a write through the slice: 99.0-1.0[-1.0, 1.0, 2.0, 3.0, 4.0, 0.0, 0.0, 0.0, 8.0, 9.0]1 8.5[3.0, 11.5, 9.5, 18.0, 19.0]
$ viv run slicing.viv
[2.0, 3.0, 4.0] [0.0, 1.0, 2.0] [7.0, 8.0, 9.0] [0.0, 3.0, 6.0, 9.0]9.0 [7.0, 8.0, 9.0]d[1] after a write through the slice: 0.0-1.0[-1.0, 1.0, 2.0, 3.0, 4.0, 0.0, 0.0, 0.0, 8.0, 9.0]1 8.5[3.0, 11.5, 9.5, 18.0, 19.0]PASS slices