Python and Viv · Recursion
Factorial
What to notice
- The recursive function reads the same in both, with braces for the body.
- Python's integers have no size limit. A Viv number is a double, which holds every integer up to 2^53 exactly; 20! is exact because its odd part is small, but 23! and beyond are rounded. The table stops at 20 so both sides print the same digits.
f"{factorial(n):d}"prints the double as a whole number, with no.0.str(n)of a Python int is its digits; Viv'sstrof a number is Python'sreprof a float (2.43290200817664e+18), so the Viv side counts the digits offmt(x, "d").- Recursion goes 2500 calls deep in every Viv engine, and a deeper call stops the run with a message; Python's default limit is 1000.
Python factorial.py · 27 lines
Viv factorial.viv · 36 lines
py2viv translated from factorial.py
What py2viv adds before the program
(nothing)
(nothing)
15 lines: a header and the guard functions its translation calls
1###
2Translated from factorial.py by py2viv 0.2.0 (numeric Python -> Viv).
3Python source sha256: 03a2977c2f122fe24d65a437f1cd1e02a43a9eda724897bd8a973bf337218a1e
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#| An int result, as Python's int: a -0.0 becomes 0 (an int has no sign on zero), and the run
9#| stops at 2^53, past which a double no longer holds every integer (Python's int does).
10def py_int(x) {
11 if abs(x) >= 9007199254740992 {
12 stop("py2viv: an integer reached 2^53; Python's int is exact there and a double is not")
13 }
14 return x + 0.0
15}
1 recursive
4def factorial(n):
5 if n <= 1:
6 return 1
7 return n * factorial(n - 1)
2def factorial(n) {
3 if n <= 1 {
4 return 1
5 }
6 return n * factorial(n - 1)
7}
20def factorial(n) {
21 if n <= 1 {
22 return 1
23 }
24 return py_int(n * factorial(py_int(n - 1)))
25}
2 with a loop
10def factorial_loop(n):
11 p = 1
12 for k in range(2, n + 1):
13 p *= k
14 return p
10def factorial_loop(n) {
11 p = 1
12 for k from 2 to n + 1 {
13 p *= k
14 }
15 return p
16}
27def factorial_loop(n) {
28 p = 1
29 for k from 2 to py_int(n + 1) {
30 p = py_int(p * k)
31 }
32 return p
33}
3 a table
17LAST = 20
18STRIDE = 5
19for n in range(0, LAST + 1, STRIDE):
20 print(f"{n:2d}! = {factorial(n):d}")
19LAST = 20
20STRIDE = 5
21for n from 0 to LAST + 1 by STRIDE {
22 print(f"{n:2d}! = {factorial(n):d}")
23}
35LAST = 20
36STRIDE = 5
37for n from 0 to py_int(LAST + 1) by STRIDE {
38 print(fmt(n + 0.0, "2d"), "! = ", fmt((factorial(n)) + 0.0, "d"), sep="")
39}
4 the number of digits of 20!
23print(len(str(factorial(LAST))), "digits")
26print(len(fmt(factorial(LAST), "d")), "digits")
41print(fmt((len(fmt((factorial(LAST)) + 0.0, "d"))) + 0.0, "d"), "digits")
5 checks
26for n in range(LAST + 1):
27 assert factorial(n) == factorial_loop(n)
29test "two factorials agree" {
30 for n to LAST + 1 {
31 assert_true(factorial(n) == factorial_loop(n))
32 }
33}
43for n to py_int(LAST + 1) {
44 if not (factorial(n) == factorial_loop(n)) {
45 stop("AssertionError")
46 }
47}
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 factorial.py
0! = 15! = 12010! = 362880015! = 130767436800020! = 243290200817664000019 digits
$ viv run factorial.viv
0! = 15! = 12010! = 362880015! = 130767436800020! = 243290200817664000019 digitsPASS two factorials agree
$ viv run factorial.py (py2viv’s translation)
The translation does not print what the Python prints: it stops with a guard where a double cannot give Python’s answer.
0! = 15! = 12010! = 362880015! = 1307674368000error: py2viv: an integer reached 2^53; Python's int is exact there and a double is not--> translated.viv:12:912 | stop("py2viv: an integer reached 2^53; Python's int is exact there and a double is not")^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ here