Skip to main content
deleted 1 character in body
Source Link
Sp3000
  • 62.3k
  • 13
  • 117
  • 292

Python 2, 158157 bytes

def f(s,o=0,d=0,D={}):T=s,o,d;x=D[T]=D[T]if T in D else~o<0andelse~o and 0**o+sum(f(s[1:],cmp(c,"[")%-3-~o,d or cmp(c,s[0]))for c in"+,-.<>[]")if s else~d<0==o;return+x 

Python 2, 158 bytes

def f(s,o=0,d=0,D={}):T=s,o,d;x=D[T]=D[T]if T in D else~o<0and 0**o+sum(f(s[1:],cmp(c,"[")%-3-~o,d or cmp(c,s[0]))for c in"+,-.<>[]")if s else~d<0==o;return+x 

Python 2, 157 bytes

def f(s,o=0,d=0,D={}):T=s,o,d;x=D[T]=D[T]if T in D else~o and 0**o+sum(f(s[1:],cmp(c,"[")%-3-~o,d or cmp(c,s[0]))for c in"+,-.<>[]")if s else~d<0==o;return+x 
added 95 characters in body
Source Link
Sp3000
  • 62.3k
  • 13
  • 117
  • 292

Still looks pretty golfable, but I'm posting this for now. It uses recursion with a bit of caching. Annoyingly, D.get doesn't short circuit for the caching, so I can't save 9 bytes that way...

Still looks pretty golfable, but I'm posting this for now. It uses recursion with a bit of caching.

Still looks pretty golfable, but I'm posting this for now. It uses recursion with a bit of caching. Annoyingly, D.get doesn't short circuit for the caching, so I can't save 9 bytes that way...

added 1931 characters in body
Source Link
Sp3000
  • 62.3k
  • 13
  • 117
  • 292

The mapping prioritises length first, then lexicographical order over the ordering "][><.-,+" (see output examples below). The main idea is to compare prefixes. 

The variable o keeps track of the number of [ brackets still open for the current prefix, while the variable d takes one of three values indicating:

  • d = 1: The current prefix is lexicographically earlier than s. Add all programs with this prefix and length <= s,
  • d = -1: The current prefix is lexicographically later than s. Add all programs with this prefix and length < s.
  • d = 0: The current prefix is a prefix of s, so we might change d to 1 or -1 later.

For example, if we have s = "[-]" and our current prefix is p = "+", since p is later than s lexicographically we know only to add the programs starting with p which are strictly shorter than s.

To give a more detailed example, suppose we have an input program s = "-[]". The first recursive expansion does this:

 (o == 0) # Adds a program shorter than s if it's valid # For the first expansion, this is 1 for the empty program + f(s[1:], o=-1, d=1) # ']', o goes down by one due to closing bracket + f(s[1:], o=1, d=1) # '[', o goes up by one due to opening bracket + f(s[1:], o=0, d=1) # '>' + f(s[1:], o=0, d=1) # '<' + f(s[1:], o=0, d=1) # '.', d is set to 1 for this and the previous branches # since they are lexicographically earlier than s's first char + f(s[1:], o=0, d=0) # '-', d is still 0 since this is equal to s's first char + f(s[1:], o=0, d=-1) # ',', d is set to -1 for this and the later branches # since they are lexicographically later than s's first char + f(s[1:], o=0, d=-1) # '+' 

Note how we don't actually use the prefixes in the recursion - all we care about them is captured through the variables d, o and the shrinking input program s. You'll notice a lot of repetition above - this is where caching comes in, allowing us to process 100-char programs well within the time limit.

When s is empty, we look at (d>=0 and o==0), which decides whether to return 1 (count this program because it's lexicographically early/equal and the program is valid), or 0 (don't count this program).

Any situtation with o < 0 immediately returns 0, since any programs with this prefix have more ]s than [, and are thus invalid.


The first 20 outputs are:

o keep track of the number of open [ brackets, d can be in -1, 0, 1 depending on which programs we want to add, and D is a cache for storing intermediate results. I'll add a better explanation later, but the idea of d is to determine whether to add only programs which are shorter than s (current prefix is lexicographically higher than s), all programs with the current prefix (if current prefix is lexicographically lower than s), or unknown (current prefix is also a prefix of s).

The mapping prioritises length first, then lexicographical order over the ordering "][><.-,+". The first 20 outputs are:

o keep track of the number of open [ brackets, d can be in -1, 0, 1 depending on which programs we want to add, and D is a cache for storing intermediate results. I'll add a better explanation later, but the idea of d is to determine whether to add only programs which are shorter than s (current prefix is lexicographically higher than s), all programs with the current prefix (if current prefix is lexicographically lower than s), or unknown (current prefix is also a prefix of s).

The mapping prioritises length first, then lexicographical order over the ordering "][><.-,+" (see output examples below). The main idea is to compare prefixes. 

The variable o keeps track of the number of [ brackets still open for the current prefix, while the variable d takes one of three values indicating:

  • d = 1: The current prefix is lexicographically earlier than s. Add all programs with this prefix and length <= s,
  • d = -1: The current prefix is lexicographically later than s. Add all programs with this prefix and length < s.
  • d = 0: The current prefix is a prefix of s, so we might change d to 1 or -1 later.

For example, if we have s = "[-]" and our current prefix is p = "+", since p is later than s lexicographically we know only to add the programs starting with p which are strictly shorter than s.

To give a more detailed example, suppose we have an input program s = "-[]". The first recursive expansion does this:

 (o == 0) # Adds a program shorter than s if it's valid # For the first expansion, this is 1 for the empty program + f(s[1:], o=-1, d=1) # ']', o goes down by one due to closing bracket + f(s[1:], o=1, d=1) # '[', o goes up by one due to opening bracket + f(s[1:], o=0, d=1) # '>' + f(s[1:], o=0, d=1) # '<' + f(s[1:], o=0, d=1) # '.', d is set to 1 for this and the previous branches # since they are lexicographically earlier than s's first char + f(s[1:], o=0, d=0) # '-', d is still 0 since this is equal to s's first char + f(s[1:], o=0, d=-1) # ',', d is set to -1 for this and the later branches # since they are lexicographically later than s's first char + f(s[1:], o=0, d=-1) # '+' 

Note how we don't actually use the prefixes in the recursion - all we care about them is captured through the variables d, o and the shrinking input program s. You'll notice a lot of repetition above - this is where caching comes in, allowing us to process 100-char programs well within the time limit.

When s is empty, we look at (d>=0 and o==0), which decides whether to return 1 (count this program because it's lexicographically early/equal and the program is valid), or 0 (don't count this program).

Any situtation with o < 0 immediately returns 0, since any programs with this prefix have more ]s than [, and are thus invalid.


The first 20 outputs are:

deleted 1 character in body
Source Link
Sp3000
  • 62.3k
  • 13
  • 117
  • 292
Loading
Source Link
Sp3000
  • 62.3k
  • 13
  • 117
  • 292
Loading