Visualize Church numerals

9

2

Background

Visualizing λ-calculus terms

Famous lambda-juggler (and code golfer) John Tromp devised an interesting visualization of terms in the λ-calculus. In his words:

abstractions (lambdas) are represented by horizontal lines, variables by vertical lines emanating down from their binding lambda, and applications by horizontal links connecting the leftmost variables.

For example, the lambda term λf.λx.f (f (f (f x))) corresponds to the visualization:

-------------------
 |   |   |   |
-------------------
 |   |   |   |   |
 |   |   |   |----
 |   |   |----
 |   |----
 |----
 |

Read it from top to bottom:

  • The first horizontal line represents the first λ.
  • The four lines descending from it represent the fs in the body.
  • Similarly, the second horizontal line represents the second λ, and the single new line descending from it represents the x in the body.
  • The rightmost f line and the x line are connected by a horizontal line representing an application (f x).
  • The next application is (f (f x)), et cetera.

Church numerals

The Church numerals are a specific sequence of terms in the λ-calculus, taking on the following pattern:

0 = λf. λx. x
1 = λf. λx. f x
2 = λf. λx. f (f x)
3 = λf. λx. f (f (f x))
...

Task

Given an input number n, print some ASCII art that visualizes the nth Church numeral. For instance, the example above is your target output when given n = 4. For n = 0, print:

---

---
 |
 |

Test cases

Your answer must output exactly the same text (modulo trailing newlines) as this stack snippet for all integer inputs n ≥ 0:

function visualize() {
  var vin = document.getElementById('visualize-in');
  var vout = document.getElementById('visualize-out');
  var n = Number(vin.value);
  if (n < 0) n = 0;
  var line = '-'.repeat(4 * n + 3);
  var bars = function(k) { return '   |'.repeat(k).substr(2); };
  var img = [line, bars(n), line, bars(n + 1)];
  for (var i = n; i > 0; --i) img.push(bars(i) + '----');
  vout.innerHTML = img.join('\n') + '\n |';
}
<label for="visualize-in">n&nbsp;<input style="width:50px;" type="number" id="visualize-in" name="visualize-in" onchange="visualize()"/></label>
<pre style="background-color: #eff0f1" id="visualize-out"></pre>

This is , so the shortest code in bytes wins.

Lynn

Posted 2016-05-29T20:32:38.777

Reputation: 55 648

Your snippet gives me error. – Leaky Nun – 2016-05-29T23:40:07.257

@LeakyNun which browser? Some browsers don't support .repeat. – Conor O'Brien – 2016-05-29T23:49:34.813

Are trailing spaces allowed? – Loovjo – 2016-06-06T09:59:49.103

No, only trailing newlines. (This is à la anarchy golf, and I feel like it’s the best rule set for [tag:ascii-art] challenges.) – Lynn – 2016-06-06T13:02:39.303

Answers

4

Retina, 74 67 63 bytes

Byte count assumes ISO 8859-1 encoding.

.
 |  
^
$.'$*----¶
\z
¶$` |
+`(.+\|) .+$
$&¶$1----
$
¶ |
  ¶
¶

Input is in unary, using any character except linefeeds as the digit.

Try it online!

Explanation

.
 |  

We start by turning each unary digit into | (note the trailing spaces). This gives us the second line of the output (plus two trailing spaces if the input is at least 1).

^
$.'$*----¶

We match the beginning of the string in order to prepend the first line. This is done using some Retina-specific substitution features. $* repeats the character on the right as many times as its left arugment. $.' evaluates the number of characters to the right of the match. Since the match is only the beginning of the string this gives as many - as there are characters in the string and --- appends three more. The is an alias for a linefeed. So now we've got the first two lines.

\z
¶$` |

Now we append the next two lines. We do this by matching the end of the string and appending a linefeed, the entire string again and then | to get the fourth line right.

+`(.+\|) .+$
$&¶$1----

Time for the applications. The leading + makes Retina repeat this stage until the output stops changing (in this case because the regex no long matches). The regex matches the entire last line, provided it contains a | follows by a space. We capture everything up to the | (which will be the second to last) in group 1. We write the line back with $&, a linefeed, then group 1 (thereby dropping the last |) and then ----.

$
¶ |

This just adds the final line containing only a single |.

  ¶
¶

Finally we need to get rid of the trailing spaces on the second line.

Martin Ender

Posted 2016-05-29T20:32:38.777

Reputation: 184 808

2

JavaScript (ES6), 112 bytes

f=
n=>`${d=`-`.repeat(n*4+3)}
${(b=` |  `.repeat(n)).slice(0,-2)}
${d}
${b} |
${b.replace(/ \|  /g,`$' |----
`)} |`
;
<input id=i type=number min=0 oninput=o.textContent=f(this.value)>
<pre id=o></pre>

Neil

Posted 2016-05-29T20:32:38.777

Reputation: 95 035

Are all the newlines necessary? Also, is f= necessary? – NoOneIsHere – 2016-06-05T17:03:31.110

@NoOneIsHere The newlines are part of the template string. The f= is not part of the answer, it's only needed for the snippet, and isn't counted as part of the byte total. – Neil – 2016-06-05T18:30:34.427

2

Python, 201 bytes

from pylab import*
n=input()
p=array(list(' -|\n'))
a=zeros((n+5,n*4+4),int)
for k in range(n):l=4*k+5;a[:-k-1,l]=2;a[-k-2,l-3:l+1]=1
a[:,1]=2
a[1,-3]=0
a[0]=a[2]=1
a[:,-1]=3
print''.join(ravel(p[a]))

user2070206

Posted 2016-05-29T20:32:38.777

Reputation: 151

1

Python 2, 138 bytes

def f(n,a=1):
 if ~n:b="---\n";c=" |";d="  "+c;print("-"*n*4+b+c*(n>0)+d*(n-1)+"\n"+"-"*n*4+b+c+d*n+"\n")*a+c+d*(n-1)+"-"*4*(n>0);f(n-1,0)

The function contains an extra parameter, but it is for internal use only. It has a default value and should be omitted when calling the function. I hope this doesn't violate the rules.

The function draws the first 5 rows, then recursively calls itself to draw the remaining rows.

Try it online

Chuck Morris

Posted 2016-05-29T20:32:38.777

Reputation: 456