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 · Recursion

Factorial

What to notice

Open the Viv in the workbench The atlas of the Viv py2viv translates this Python; the translation stops where the Python does not (see below).
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! = 1
5! = 120
10! = 3628800
15! = 1307674368000
20! = 2432902008176640000
19 digits

$ viv run factorial.viv

0! = 1
5! = 120
10! = 3628800
15! = 1307674368000
20! = 2432902008176640000
19 digits
PASS 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! = 1
5! = 120
10! = 3628800
15! = 1307674368000
error: py2viv: an integer reached 2^53; Python's int is exact there and a double is not
--> translated.viv:12:9
12 | stop("py2viv: an integer reached 2^53; Python's int is exact there and a double is not")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ here