This site is version 0.0.1. Most of the code on this site is drafted by an agent and needs review. I apologize for the AI-speak, we are working on rewriting it!
viv-lang.org

Python and Viv · Dicts and maps

Counting with number keys

What to notice

Open the Viv in the workbench The atlas of the Viv py2viv translates this Python; the translation prints the same output.
Python counting.py · 34 lines
Viv counting.viv · 44 lines
py2viv translated from counting.py
What py2viv adds before the program
(nothing)
(nothing)

60 lines: a header and the guard functions its translation calls

1###
2Translated from counting.py by py2viv 0.2.0 (numeric Python -> Viv).
3Python source sha256: 21c43a600f53c5f52374e1f8d982f918dbc5241132a39ccbb390f140d45dfd00
4Each print(...) is one print(...) line; fmt(x) writes a float as Python's repr does.
5A `raise`, a failed assert and every guard `stop` with Python's exception line.
6###
7
8#| 0, 1, ..., n - 1.
9def py_iota(n) {
10 a = zeros(n)
11 for i to n {
12 a[i] = i
13 }
14 return a
15}
16
17#| Every entry of a real array, row-major, as a vector (a copy).
18def py_flat(a: Arr) {
19 return take(a, py_iota(numel(a)))
20}
21
22#| np.sort(a) / sorted(a): a sorted COPY. Viv's sort(a) sorts in place and returns 0.
23def py_sort(a: Arr) {
24 if any(a != a) {
25 stop("py2viv: sorting a NaN (Python's sorted and NumPy's sort each put it somewhere of their own)")
26 }
27 if any(a == 0) and any(1.0 / a == -1.0 / 0.0) and any(1.0 / a == 1.0 / 0.0) {
28 stop("py2viv: sorting 0.0 and -0.0 together (their order is the sort algorithm's)")
29 }
30 b = a * 1.0
31 sort(b)
32 return b
33}
34
35#| An int result, as Python's int: a -0.0 becomes 0 (an int has no sign on zero), and the run
36#| stops at 2^53, past which a double no longer holds every integer (Python's int does).
37def py_int(x) {
38 if abs(x) >= 9007199254740992 {
39 stop("py2viv: an integer reached 2^53; Python's int is exact there and a double is not")
40 }
41 return x + 0.0
42}
43
44#| The sum of an integer array or list, exactly: every partial sum checked below 2^53.
45def py_isum(a: Arr) {
46 f = py_flat(a)
47 s = 0
48 for i to len(f) {
49 s = py_int(s + f[i])
50 }
51 return s
52}
53
54#| d[k] of a dict: Python's KeyError when k is not a key.
55def py_mget(m: Map, k) {
56 if not has(m, k) {
57 stop("KeyError")
58 }
59 return m[k]
60}
1 count the sums of two dice
4SIDES = 6
5sums = {}
6for a in range(1, SIDES + 1):
7 for b in range(1, SIDES + 1):
8 sums[a + b] = sums.get(a + b, 0) + 1
9print(sums)
2SIDES = 6
3sums = map()
4for a from 1 to SIDES + 1 {
5 for b from 1 to SIDES + 1 {
6 sums[a + b] = get(sums, a + b, 0) + 1
7 }
8}
9print(sums)
65SIDES = 6
66sums = map()
67for a from 1 to py_int(SIDES + 1) {
68 for b from 1 to py_int(SIDES + 1) {
69 sums[(py_int(a + b)) + 0.0] = (py_int(get(sums, py_int(a + b), 0) + 1)) + 0.0
70 }
71}
72print(fmt(sums, "d", "d"))
2 a bar per key, in key order
12for total in sorted(sums):
13 print(f"{total:2d} {'#' * sums[total]}")
12totals = keys(sums)
13sort(totals)
14for total in totals {
15 print(f"{total:2d} {"#" * sums[total]}")
16}
74py_s1 = py_sort(keys(sums))
75for py_i2 to len(py_s1) {
76 total = py_s1[py_i2]
77 print(fmt(total + 0.0, "2d"), ("#" * py_mget(sums, total)))
78}
3 the most likely sum
16best = max(sums, key=lambda k: sums[k])
17print("most likely:", best, "with", sums[best], "of", SIDES * SIDES)
19best = max(keys(sums), key=fn(k) -> sums[k])
20print("most likely:", best, "with", sums[best], "of", SIDES * SIDES)
80py_f5 = false
81py_m3 = 0
82py_k4 = 0
83py_s6 = keys(sums)
84for py_i7 to len(py_s6) {
85 py__py2viv_item8 = py_s6[py_i7]
86 py_y9 = py_mget(sums, py__py2viv_item8)
87 if not py_f5 {
88 py_m3 = py__py2viv_item8
89 py_k4 = py_y9
90 py_f5 = true
91 }
92 else if py_y9 > py_k4 {
93 py_m3 = py__py2viv_item8
94 py_k4 = py_y9
95 }
96}
97if not py_f5 {
98 stop("ValueError: max() arg is an empty sequence")
99}
100best = py_m3
101print("most likely:", fmt(best + 0.0, "d"), "with", fmt((py_mget(sums, best)) + 0.0, "d"), "of", fmt((py_int(SIDES * SIDES)) + 0.0, "d"))
4 first occurrence of each value
20data = [4, 7, 4, 1, 7, 7, 9]
21first_at = {}
22for i, x in enumerate(data):
23 if x not in first_at:
24 first_at[x] = i
25print(first_at)
23data = [4, 7, 4, 1, 7, 7, 9]
24first_at = map()
25for i, x in enumerate(data) {
26 if x not in first_at {
27 first_at[x] = i
28 }
29}
30print(first_at)
103data = [4, 7, 4, 1, 7, 7, 9]
104first_at = map()
105for i to len(data) {
106 x = data[i]
107 if not has(first_at, x) {
108 first_at[x + 0.0] = i + 0.0
109 }
110}
111print(fmt(first_at, "d", "d"))
5 a copy is a second map
28backup = dict(first_at)
29first_at[100] = 0
30print(len(first_at), len(backup))
33backup = copy(first_at)
34first_at[100] = 0
35print(len(first_at), len(backup))
113backup = copy(first_at)
114first_at[100] = 0
115print(fmt((len(first_at)) + 0.0, "d"), fmt((len(backup)) + 0.0, "d"))
6 checks
33assert sum(sums.values()) == SIDES * SIDES
34assert first_at[7] == 1
38test "counts" {
39 assert_true(sum(values(sums)) == SIDES * SIDES)
40 assert_true(first_at[7] == 1)
41}
117if not (py_isum(values(sums)) == py_int(SIDES * SIDES)) {
118 stop("AssertionError")
119}
120if not (py_mget(first_at, 7) == 1) {
121 stop("AssertionError")
122}

What they print

identical — the two programs print the same text.

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.

$ python3 counting.py

{2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 5, 9: 4, 10: 3, 11: 2, 12: 1}
2 #
3 ##
4 ###
5 ####
6 #####
7 ######
8 #####
9 ####
10 ###
11 ##
12 #
most likely: 7 with 6 of 36
{4: 0, 7: 1, 1: 3, 9: 6}
5 4

$ viv run counting.viv

{2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 5, 9: 4, 10: 3, 11: 2, 12: 1}
2 #
3 ##
4 ###
5 ####
6 #####
7 ######
8 #####
9 ####
10 ###
11 ##
12 #
most likely: 7 with 6 of 36
{4: 0, 7: 1, 1: 3, 9: 6}
5 4
PASS counts

$ viv run counting.py (py2viv’s translation)

The translation prints exactly what the Python prints.

{2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 5, 9: 4, 10: 3, 11: 2, 12: 1}
2 #
3 ##
4 ###
5 ####
6 #####
7 ######
8 #####
9 ####
10 ###
11 ##
12 #
most likely: 7 with 6 of 36
{4: 0, 7: 1, 1: 3, 9: 6}
5 4