APL (158 characters, score = 4)
'''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0
I'm using Dyalog APL here. The number of cycles can be increased by one by adding 0
(0 followed by a space) to the end of the expression and to the end of the string (before '''
). The cycle length is (# 0's) + 1
, and the length of the expression is 150 + 4*(cycle length))
. Assuming we continue adding zeros forever, the score is Limit[(150 + 4*n)/(n - 1), n -> Infinity] = 4
, where n
is the cycle length.
Here's an example with cycle length=6:
'''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0
0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0
0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0
0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0
0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0
0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0
0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0
0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0
0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0
0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1
0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1
'''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 0 0 0 0
192 characters, score = 2
'''{2≠⍴⍺:¯3⌽(2×1+⍴⍺)⍴(1+⍴⍺)⍴⍺ ⋄ a←⊃2⌷⍺ ⋄ ⍵=0:¯2⌽(2×1+⍴a)⍴(1+⍴a)⍴a⋄(-4+⌊10⍟⊃⍺)⌽(2×1+⍴a)⍴(1+⍴a)⍴a}01'''{2≠⍴⍺:¯3⌽(2×1+⍴⍺)⍴(1+⍴⍺)⍴⍺⋄a←⊃2⌷⍺⋄⍵=0:¯2⌽(2×1+⍴a)⍴(1+⍴a)⍴a⋄(-4+⌊10⍟⊃⍺)⌽(2×1+⍴a)⍴(1+⍴a)⍴a}01
Depending on the implementation, one point of failure could be when the integer prefixed to the string is too large. Theoretically, though, we can add a cycle by adding two characters - a 1
at the end of the string (before '''
) and a 1
at the end of the entire line.
200 characters, score = 1
'''{a←{2=⍴⍵:⊃2⌷⍵⋄⍵}⍺⋄(⍺{⍵=9:⍬⋄⍕1+{2=⍴⍵:10×⊃⍵⋄0}⍺}⍵),(¯2⌽(2×1+⍴a)⍴(1+⍴a)⍴a),⍺{⍵=9:(⍕9),⍕⊃⍺⋄⍕⌊⍵÷10}⍵}'''{a←{2=⍴⍵:⊃2⌷⍵⋄⍵}⍺⋄(⍺{⍵=9:⍬⋄⍕1+{2=⍴⍵:10×⊃⍵⋄0}⍺}⍵),(¯2⌽(2×1+⍴a)⍴(1+⍴a)⍴a),⍺{⍵=9:(⍕9),⍕⊃⍺⋄⍕⌊⍵÷10}⍵}91
My APL implementation doesn't have unlimited precision integers by default, so the integer is converted to a float when it becomes too large, causing the output to be wrong. So this one is the most finicky, but theoretically (either by hand or with a different APL interpreter), it should have a score of 1. Just add a 1
to the end of the expression, and you get another cycle.
Overview (with a shorter quine)
I'm going to give an overview of the first version, because I think it's probably the easiest to comprehend. Before tackling that version, however, we're going to consider a simple quine in APL:
1⌽22⍴11⍴'''1⌽22⍴11⍴'''
I've found that one of the best ways to understand some APL expressions is to look at the output throughout the cascade of operators/functions. All operators and functions in APL are right-associative and have the same precedence, so here is is, from right to left:
'''1⌽22⍴11⍴'''
: This is just a string literal (a list of characters). ''
is the APL way of escaping single quote marks. Output: '1⌽22⍴11⍴'
.
11⍴'''1⌽22⍴11⍴'''
: Here, we reshape (⍴
) the string to be of length 11
. Because the string's length is under 11, it is repeated (i.e., 5⍴'abc'
would yield 'abcab'
). Output: '1⌽22⍴11⍴''
. So we now have two quotation marks at the end - we're getting somewhere!
22⍴11⍴'''1⌽22⍴11⍴'''
: Similarly, we now reshape our previous output to be of length 22
. Output: '1⌽22⍴11⍴'''1⌽22⍴11⍴''
. We're almost there - we just need to move the first single quote to the end.
1⌽22⍴11⍴'''1⌽22⍴11⍴'''
: Here, we rotate (⌽
) the list of characters by 1
. This moves the first character of the string to the end. As another example, 2⌽'abcdef'
returns 'cdefab'
. Output: 1⌽22⍴11⍴'''1⌽22⍴11⍴'''
.
The rotating quine
That short quine is the main basis for our rotating quine. Now, with that in mind, let's take a look at our quine:
'''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0
{ ... }
defines an unnamed function, which is where we'll be doing the work. Note that functions in APL take a right argument, denoted by ⍵
, and an optional left argument, denoted by ⍺
(think infix). We want to feed this function both our quine string and something to aid us in creating an arbitrary number of cycles. To make things easier on ourselves (and anyone wanting to add cycles), we make the quine string the left argument. The right argument, then, is where we put our list of cycles. 2 or more items separated by a space creates a list, so in this example, we have 2-element list consisting of a 1
and a 0
.
We can see that the function looks similar to the quine from before. We have the same ...⌽...⍴...⍴...
form from before. So that's good - we at least understand that much! Let's delve deeper into the ellipses, starting with everything after the last ⍴
: ⊃,/(~^/¨⍺=0)/⍺
.
- As you can see by looking at the example above, we prefix the string with the 0's from the right-hand side, adding one with each iteration; but we don't care about those right now. We just want the string!
- First, consider what's in the parentheses. (They group like in most other languages, by the way.)
⍺=0
returns a list, in this case, with the same shape as ⍺
, where each element in ⍺
is replaced by a 1
if it is equal to 0
, and a 0
otherwise. This is performed recursively; so if we have a list of a list of a list of characters, the individual characters will be tested against 0, and you will get back a list of a list of a list of binary values.
- So if
⍺
consists of just our string, we get back a list of 0's. Otherwise, our left argument has some 0's prefixed to it (e.g., 0 0 0 'quinestring'
), so it is a list consisting of 0's and another list, our string. Then our output looks like 1 1 1 <sub-list of zeros>
.
^/¨⍺=0
: We apply the derived function ^/
, which reduces (/
) using the logical AND (^
) function, to each (¨
) element of ⍺=0
. This is to flatten the sub-list of zeros so that we can consider the quine string to be one binary value. Considering the previous example, the output would be 1 1 1 0
.
~
: We binary NOT each of the values from before (e.g., returning 0 0 0 1
).
(~^/¨⍺=0)/⍺
: For each element in ⍺
, we replicate (/
) it the number of times given by the corresponding element in the left argument. This eliminates all of the 0's, leaving us only with our quine string.
⊃,/
is some necessary paperwork to ensure that we get back a flattened list of characters, by reducing the result with the concatenation function (,
). If the input is already a flattened list (i.e., the left argument to our main function is only the string), we get a 1-element list containing that list. In the other case, when we have a list consisting of a sub-list for the string, we get the same thing back (a list with a sub-list). We then unpack this (⊃
), giving us only the first element of the list (i.e., the sub-list of characters). This might seem unnecessary, but otherwise we would then be trying to reshape a 1-element list!
Next, we look at the length given for the first reshape, within the parentheses:
⍺,⍵
: We concatenate the right argument to the first argument
⊃,/⍺,⍵
: Same as before - flatten the list.
+/0=⊃,/⍺,⍵
: Add up the number of zeros in the list by reducing (/
) using the addition (+
) function.
2×+/0=⊃,/⍺,⍵
: Multiply that number by two.
z←2×+/0=⊃,/⍺,⍵
: Assign (←
) the result to a variable, z
. To recap, z
is now twice the number of zeros found in both the left and right arguments.
77+z←2×+/0=⊃,/⍺,⍵
: We then add 77
, for the characters in the quine string, ignoring everything after the space following 1
. Like in the initial quine example, we add 1 to the length of the string to get another single quote.
- The output of this reshape, in this example, is:
'{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 ''
The argument to the reshape that follows is simple and mirrors the short quine (2 times the length for the first reshape). Our output now is:
'{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 '''{(((3+z)×^/⍵)-5+2×+/+/¨⍺=0)⌽(2×77+z)⍴(77+z←2×+/0=⊃,/⍺,⍵)⍴⊃,/(~^/¨⍺=0)/⍺}1 0 ''
Now for the final step, where we calculate how much to rotate the output string:
- As you can see by looking at the previous output, we want to rotate it back (a negative amount) to bring the 2 final quotes to the beginning. Because we want a
0
(and another space) to move to the beginning as well, we want to rotate it an additional 3 characters back.
+/+/¨⍺=0
: Add up the number of zeros in the left argument. The first (from the right) +/¨
sums each element's count (i.e., a sublist or just an integer), and the second +/
gives us the sum of that resulting list.
5+2×+/+/¨⍺=0
: Multiply by two (to rotate the spaces as well), and add 5 (the result we came up with before).
- Now, we subtract the previous value from the left argument to
-
to handle the case when we hit the end of our cycle:
(3+z)×^/⍵
: AND all of the elements in the right argument together to see if we've reached our end (1
), and multiply that by 3+z
.
And we're done!
world! Hello,
isn't a rotation ofHello, world!
;world!Hello,
is. \$\endgroup\$