C(170 个字符)
i,j;main(c,s){char**r=s,*p=*++r;for(;i<3;)j--?putchar(!p[-1]?p=*r,++i,j=0,10:
"##3#3133X=W.<X/`^_G0?:0@"[i*8+c/2]-33>>c%2*3+j&1?"|_"[j&1]:32):(j=3,c=*p++&31,
c-=c>6?10:1);}
这将输入字符串作为命令行参数。转换为使用 stdin 将是另一个字符:
i,j;main(c){char s[99],*p=s;for(gets(s+1);i<3;)j--?putchar(!*p?p=s,++i,j=0,10:
"##3#3133X=W.<X/`^_G0?:0@"[i*8+c/2]-33>>c%2*3+j&1?"|_"[j&1]:32):(j=3,c=*++p&31,
c-=c>6?10:1);}
stdin 版本最多可以接受 98 个输入字符。当然,任何超过floor(terminalWidth / 3)
都会导致令人困惑的换行。
每个字符的输出被视为 3x3 网格,其中每行中的单元格是段。段是“开”或“关”。如果段为“on”,则输出 a'|'
或 a '_'
,具体取决于位置。如果它关闭,则输出一个空格。字符数组是一个位数组,用于确定每个段是打开还是关闭。更多关于代码后的内容:
i,j; /* Loop variables. As globals, they'll be initialized to zero. */
main(c,s){
/* The signature for main is
*
* main(int argc, char **argv)
*
* Rather than add more characters for properly declaring the parameters,
* I'm leaving them without type specifiers, allowing them to default to
* int. On almost all modern platforms, a pointer is the same size as
* an int, so we can get away with the next line, which assigns the int
* value s to the char** variable r.
*/
char**r=s,*p=*++r;
/* After coercing the int s to a char** r, offset it by 1 to get the
* value of argv[1], which is the command-line argument. (argv[0] would
* be the name of the executable.)
*/
for(;i<3;) /* loop until we're done with 3 lines */
j--?
/* j is our horizontal loop variable. If we haven't finished a
* character, then ... */
putchar( /* ...we will output something */
!p[-1]? /* if the previous char was a terminating null ... */
p=*r,++i,j=0,10
/* ... reset for the next row. We need to:
*
* - reinitialize p to the start of the input
* - increment our vertical loop variable, i
* - set j to zero, since we're finished with this
* "character" (real characters take 3 iterations of
* the j loop to finish, but we need to short-circuit
* for end-of-string, since we need to output only one
* character, the newline)
* - finally, send 10 to putchar to output the newline. */
:"##3#3133X=W.<X/`^_G0?:0@"[i*8+c/2]-33>>c%2*3+j&1?
/* If we haven't reached the terminating null, then
* check whether the current segment should be "on" or
* "off". This bit of voodoo is explained after the
* code. */
"|_"[j&1]:32
/* if the segment is on, output either '|' or '_',
* depending on position (value of j), otherwise,
* output a space (ASCII 32) */
)/* end of putchar call */
:(j=3,c=*p++&31,c-=c>6?10:1);
/* this is the else condition for j--? above. If j was zero,
* then we need to reset for the next character:
*
* - set j to 3, since there are three cells across in the grid
* - increment p to the next input character with p++
* - convert the next character to a value in the range 0–15.
* The characters we're interested in, 0–9, A–F, and a–f, are
* unique in the bottom four bits, except the upper- and
* lowercase letters, which is what we want. So after anding
* with 15, the digits will be in the range 16–25, and the
* letters will be in the range 1–6. So we subtract 10 if
* it's above 6, or 1 otherwise. Therefore, input letters
* 'A'–'F', or 'a'–'f' map to values of c between 0 and 5,
* and input numbers '0'–'9' map to values of c between
* 6 and 15. The fact that this is not the same as the
* characters' actual hex values is not important, and I've
* simply rearranged the data array to match this order.
*/
}
字符数组描述字符网格。数组中的每个字符描述了两个输入字符的输出网格的一个水平行。网格中的每个单元格由一位表示,其中1
表示该段为“on”(因此输出 a'|'
或 a '_'
,具体取决于位置),并0
表示该段为“off”。
它需要数组中的三个字符来描述两个输入字符的整个网格。数组中每个字符的最低三位,即 0-2 位,描述了这两个字符中偶数输入字符的一行。接下来的三位,即位 3-5,描述了两位中奇数输入字符的一行。第 6 位和第 7 位未使用。这种排列,偏移量为 +33,允许数组中的每个字符都是可打印的,没有转义码或非 ASCII 字符。
我玩弄了几种不同的编码,包括将输入字符的所有 7 个段的位放入数组中的一个字符中,但发现这个是整体最短的。虽然此方案需要数组中的 24 个字符来表示仅 16 个输入字符的段,但其他编码要么需要使用非 ASCII 字符(当我在我的摩尔斯电码高尔夫答案中使用它时,不出所料会导致问题),很多转义码, 和/或复杂的解码代码。这个方案的解码代码非常简单,尽管它确实充分利用了 C 的运算符优先级来避免添加任何括号。
让我们把它分解成小步骤来理解它。
"##3#3133X=W.<X/`^_G0?:0@"
这是编码数组。让我们抓取适当的字符进行解码。
[i*8
前 8 个字符描述顶行段,接下来的 8 个描述中间行段,最后 8 个描述底行段。
+c/2]
请记住,此时,c 包含一个从 0 到 15 的值,它对应于 ABCDEF0123456789 的输入,并且该数组对每个编码字符编码两个输入字符。因此,数组中的第一个字符'#'
保存了“A”和“B”的顶行的位,第二个字符也'#'
编码了“C”和“D”的顶行,依此类推。
-33
编码会产生几个小于 32 的值,这需要转义码。这个偏移量将每个编码字符带入可打印的非转义字符范围。
>>
右移运算符的优先级低于算术运算符,因此此移位是在减去偏移量后对字符进行的。
c%2*3
c%2
对偶数计算为零,对奇数计算为一,因此我们将对奇数字符右移 3,得到第 3-5 位,对偶数字符完全不移动,提供对第 0-2 位的访问. 虽然我更喜欢使用c&1
偶数/奇数检查,这也是我在其他地方使用的,但&
运算符的优先级太低,无法在此处使用而不添加括号。%
运算符具有正确的优先级。
+j
移动额外的j
位以获得当前输出位置的正确位。
&1
按位和运算符的优先级低于算术运算符和移位运算符,因此这将测试在移位使相关位变为位零之后是否设置了位零。
?
如果位零设置...
"|_"
...输出这些字符之一,由...选择
[j&1]
...我们的水平循环变量是偶数还是奇数。
:32
否则(未设置位零),输出 32(空格字符)。
如果有的话,我不认为我可以将其进一步精简,当然也不足以击败hobbs 的 perl entry。