创建一个呈现其自身源代码的“黑客打字机”程序


25

如果您不熟悉hacker typer,请参阅hackertyper.net。简而言之,它是一个程序,每次击键都会输出一个代码库的一部分,以实现喜剧效果。但是,hackertyper.net版本太容易实现了。它只是从任意一段代码中一次输出三个字符。为了应对这一挑战,程序必须输出自己的源代码,并在每次击键时打印一个空格分隔的代码块。

细节

  • 不能为程序的文件名硬编码。它必须动态确定其名称。如果程序编译为可执行文件,则可以将标准文件扩展名附加到可执行文件的名称(如果使用Windows,则不包括.exe),并假定源文件在可执行文件的目录内。例如,如果C可执行文件名为“ hacker”,则它应从同一目录中名为“ hacker.c”的文件中提取其源代码。如果已编译程序具有扩展名,则应在确定其源代码名称之前将其删除(“ typer.exe”->“ typer.cs”)。
  • 程序必须包含至少5个空格,每个空格之间至少要有一个字符。这意味着此挑战的最小可能大小为9个字节。空格对于程序的功能不必很关键。
  • 任何格式(缩进,换行等)都必须保留在输出中。可以使用继续执行该代码的格式来打印该格式,也可以跟随其后的代码打印,重要的是要保持格式。
  • 除非使用其他语言来实现空格,否则请避免使用注释来满足5个空格的要求。

编辑:可以使用新行来代替空格,也可以将空格用作块分隔符。


1
我有点困惑。该程序是否应该是quine?
Orby 2014年

8
您所描述的方式听起来似乎可以接受从原始源文件中读取代码,而这并不是一个问题。我认为,如果程序必须是实际的方法,那将是一个更好的竞赛。
Orby

1
@Orby我要说的是,该程序不是传统意义上的标准程序,无论是否允许读取源代码。Quines没有输入,但是这些程序显然有输入。
卡尔文的爱好2014年

@DrJPepper您的第三个要点是听起来像任何空格计数序列都作为分隔符,但是您特别要说只有空格。你能澄清一下吗?
卡尔文的爱好2014年

2
这一挑战鼓励阅读程序自身的源代码,这种做法通常是在构建quines时编写的。
feersum 2014年

Answers:


13

bash,51 58

for w in $(<$0);do read -sn 1;printf -- "$w ";done

2
它的庆典,没有壳:这不会仪表下方,(工作2: read: Illegal option -s
F. Hauri

1
假设bash可以替换cat $0$(<$0)

@broslow thx以获取反馈;标有bash的长度相同

1
@将没问题。IFS=\ 如果省略shebang,是真的需要吗?默认IFS类似于IFS=$'\n\t ',并且由于您不再拥有换行符,因此我认为您不必将其限制为仅空格。

1
for w in `<$0`;{ read \-sn1;printf $w\ ;}
jimmy23013 2015年

21

HTML&JavaScript,123

<head></head><body onload="s=(a=document.all)[i=0].innerHTML" onkeyup="a[2].textContent += s.split(/(?= )/)[i++%6]"></body>

这与骇客打字机类似,但有其自己的源代码。如果我误解了规则,请告诉我。

这是样式版本(170个字符):

<head></head>
<body style="background:#000;color:lime" onload="s=(a=document.all)[i=0].innerHTML" onkeyup="a[3].textContent+=s.split(/(?=\s)/)[i++%6]">
<pre></pre></body>

我做了一个演示。由于JS Bin添加了很多额外的代码,所以对其进行了修改,但是总体思路是相同的。


2
如果没有<html>和<head>标记并且没有结束</ body>不能正确呈现,我会感到惊讶。您会惊讶于所有浏览器在这方面的容忍度。
2014年

2
@会谢谢。我包括的原因<head>是,如果浏览器不存在,浏览器会添加它,因此它将始终显示。我忘了<html>
grc 2014年

12

Perl + Term :: ReadKey,56个字节

use
Term'ReadKey;ReadMode
4;open
0;ReadKey,print
for
<0>

感谢ThisSuitIsBlackNot的原始灵感,以及primo的建议open 0<0>

请注意,后面的换行符for实际上是不必要的,只是我需要在某处添加一个额外的换行符以使空格计数达到指定的最小值5。

还要注意,像ThisSuitIsBlackNot的提交一样,该程序需要CPAN 的Term :: ReadKey模块。在Debian / Ubuntu Linux上,可以使用以下命令轻松安装该模块(如果尚不存在)sudo apt-get install libterm-readkey-perl

另外,为了节省一些字符,该程序在退出时不会将输入模式恢复为正常状态,因此您可能会发现自己之后看不到键入的内容。执行shell命令stty sanereset应该解决该问题。此问题可以通过以下方式解决,以增加10个字节为代价:

use
Term'ReadKey;ReadMode
4;open
0;ReadKey,print
for<0>;ReadMode
0

奖励:纯粹的quine,81个字节

$_=q{use
Term'ReadKey;ReadMode
4;ReadKey,say
for
split$/,
"\$_=q{$_};eval"};eval

同样,逗号后的换行符仅需满足五个空格的最小值。

与上面的56字节程序不同,此版本实际上不需要读取自己的源代码,因为它基于一个quine-特别是基于此quine:

$_=q{say"\$_=q{$_};eval"};eval

关于这种方法的好处是,它可以轻松地在q{ }块中携带任意的“有效载荷” ,而不必重复执行。虽然它不能<0>在短时间内打败,但确实可以接近。

注意:此程序使用Perl 5.10+ say功能,因此需要使用-M5.010(或-E)命令行开关来调用。根据对meta的公认共识,用于启用现代语言功能的此类开关不算作多余的字符。没有我可以找到的最短解决方案say是83个字节:

$_=q{use
Term'ReadKey;ReadMode
4;ReadKey,print
for
split/^/,
"\$_=q{$_};eval"};eval

通过(加入最后两行并)插入,还可以使这两种方式对终端更友好:

;ReadMode
0

在最后之前}


哇。哇 很酷。
ThisSuitIsBlackNot

+1,但我建议养成键入stty sane而不是输入的习惯reset(在某些操作系统上,这有时可能会做一些事情,而不仅仅是重置某些终端参数^^)
Olivier Dulac

非常好的解决方案。FWIW,open F,$0并且<F>可以用open 0和代替<0>。另外,我认为元数据中的一个帖子并不能真正构成共识。正如作者所建议的那样,该选项-M5.01不会“将语言带入特定点”,而是启用了功能。没有默认启用这些功能的perl版本。
primo

3
@primo:如果您不同意现有的答案,请务必将您自己的答案发布到meta线程上。到目前为止,三年半内没有人这样做的事实确实表明了合理程度的共识,至少在积极访问meta的常客中,但共识总是可以改变的。(无论如何,按照我的观点,如果ruby golfscript.rb foo.gs将其视为运行用GolfScript编写的程序的有效命令,perl -M5.010 foo.pl则应将其视为运行以“ Perl 5.10”编写的程序的有效命令。但是,此类参数确实属于meta,而不是在这里。)
Ilmari Karonen 2014年

5

Python 3-124字节-7个空格


码:

from curses import*
s=initscr();noecho()
for x in open(__file__).read().split(" "):s.getch();s.addstr(x+" ")
echo();endwin()

取消高尔夫:

from curses import*
# init curses
screen=initscr()
noecho()
# split code into spaces
code = open(__file__).read().split(" ")
for x in code:
    # wait for keypress
    screen.getch()
    # print a bit
    screen.addstr(x+" ")
# deactivate curses
echo()
endwin()

样式版本:

from curses import*
s=initscr();noecho();start_color();init_pair(2,COLOR_GREEN,COLOR_BLACK)
for x in open(__file__).read().split(" "):s.getch();s.addstr(x+" ",color_pair(2))
echo();endwin()

4

红宝石,85,71

require"io/console";f=File.open __FILE__;loop{STDIN.raw &:getc;print f.read(3)||exit}

太糟糕了,IO#raw它不是标准库的一部分。

改善

require"io/console";24.times{|q|STDIN.raw &:getc;$><<IO.read($0,3,q*3)}

这消除了对Kernel#exit的调用,并使用全局变量来缩短代码。


4

Befunge-21

~ $ g , 1 +:54*`#@_:0

我对此感到非常满意,因为我刚刚发现了Befunge。如果您不介意在弹出窗口中“键入”,则可以在此处此处运行它直到找到更好的在线解释器为止。


2

Powershell,89岁

(gc $MyInvocation.MyCommand.Path).split(" ")|%{$l+="$_ ";write-host "$l";read-host ;cls}

2

Python 3-299

a="""from curses import*
s=initscr()
raw()
noecho()
for x in e:
 s.getch()
 s.addstr(x+' ')
nocbreak()
echo()
endwin()
""";b="""e=(a+'a=#%s#;b=#%s#;%s'%(a,b,b.replace('#','""''"',4))+'exec(a)').split(' ')
""";e=('a="""%s""";b="""%s""";%s'%(a,b,b.replace('#','""''"',4))+'exec(a)').split(' ')
exec(a)

它是一个奎因。通过使用exec和移动一些语句从507缩短。


2

C,211个 186字节

我在C中使用curses库解决方案。它可能比其他C解决方案要长,但这是一个问题。尽管这个问题没有要求,但它仍然相当不错。它也可以很好地工作:

#define R(x)#x
#define T(x)R(x)
#define S(p)char*s="#define R(x)#x\n#define T(x)R(x)\n#define S(p)"p"\nS(T(S(p)))";main(){initscr();noecho();while(*s)if(~getch())addch(*s++);}
S(T(S(p)))

具有更多注释和内容的可读性更高的版本:

#define R(x)#x /* macros to convert the source code in S into a C-string */
#define T(x)R(x)
#define S(p) char*s="#define R(x)#x\n" \
                    "#define T(x)R(x)\n" \
                    "#define S(p) " p "\n" \
                    "S(T(S(p)))";\
    main(){\
        initscr();\
        noecho(); /* don't echo input */ \
        while(*s)\
            if(~getch()) /*true if character has been typed */ \
                addch(*s++);\
}
S(T(S(p)))

编译:

gcc -o h h.c -lncurses

2

C- 136135132字节(仅Windows)

*fopen();**v;b[ 1<<20];main(p,q){v=q; strcpy(b,*v);strcat(b,".c") ;for(*v=fopen(b,"r");~fscanf(*v,"%s",b);printf("%s ",b))getch();} 

注意:程序末尾有一个空格,可能不会显示。

我不能保证此程序可以在我自己的计算机以外的其他计算机上运行,​​因为它确实很hacky。当每个人只有32位计算机时,事情本来要简单得多。然后,我不必担心会sizeof(int*)是8岁(这肯定是;我打印出来以确保)sizeof(int)是4。

幸运的是,可执行文件的名称存储在argv中的第一个字符串中。但是,将指针作为函数的参数表示这意味着我必须明确指定该函数的所有参数的类型(这意味着我必须键入int两次),这极大地浪费了字符。幸运的是,我找到了一种解决方法。我对main的第二个论点q是另一个int。然后qint**以某种方式分配给一个类型的变量,设法从堆栈中获取所有必需的字节。

我没有找到任何这样的技巧来在fopen不声明函数的情况下将其返回类型解释为指针,但未成功。

编辑:注意到我应该使用~fscanf(*v,"%s",b)而不是fscanf(*v,"%s",b)>0因为到达EOF时返回值为-1。


这对于我来说是段错误,因此我无法对其进行测试,但是您应该能够声明一个void指针(void **v;)而不是进行原型设计fopen()
Comintern

@Comintern所做的更改没有帮助我正确存储的结果fopen。我不明白为什么用void代替int会有所作为,因为所有指针的大小都相同。
feersum

好点子。更短,更稳定,只是声明指针虽然-这其实对我来说运行:b[1<<20];main(int *c,char **v){strcpy(b,*v);strcat(b,".c");c=fopen(b,"r");for(;fscanf(c,"%s",b)>0;printf("%s ",b))getch();}(我不得不替代getchar()getch(),虽然)。
Comintern

@Comintern您的代码仍然在我的系统上崩溃,但是让它正常工作很不错。我想这就像我说的那样-该程序的每个版本都可以在1台计算机上运行。
feersum

您为什么不使用K&R原型?例如*fopen()而不是*fopen(a,b)
FUZxxl 2015年

1

Perl-87字节

#!/usr/bin/perl -040
use Term::ReadKey;open F,$0;ReadMode 3;print''.<F>while ReadKey 0

一旦读取完文件后,我在规则中看不到任何操作,因此它只是在打印最后一块之后等待输入。


1

使用LiveScript的node.js:

#!/usr/local/bin/lsc
console.log <| require \fs .readFileSync __filename, encoding: \utf8

异步版本:

#!/usr/local/bin/lsc
require \fs .readFile __filename, encoding: \utf8, -> console.log &1

1

眼镜蛇-147

class P
    def main
        while 1,for a in File.readLines(CobraCore.exePath[:-4]+'.cobra'),print if('[Console.readKey]'and (Console.cursorLeft=0)<1,a,'')*

CobraCore.exePath 太有用了!


1

Javascript ES6、154

Firefox 154

(a= (i=1,b="(a= "+a+")()",s="") => {window.onkeydown=()=>{clear();i=b.indexOf(" ",i+1),d=b.slice(0,i<0?b.length:i);console.log(s+d);if(i<0){i=0,s+=d}}})()

镀铬175

( a= function (i,s){b="( a= "+a+")()";c=console,window.onkeydown=function(){c.clear();s=s||"",i=b.indexOf(" ",i+1),d=b.slice(0,i<0?b.length:i);c.log(s+d);if(i<0){i=0,s+=d}}})()

两者274

( a= function (i,s){b="( a= "+a+")()";c=console,window.onkeydown=function(){(clear)?clear():c.clear?c.clear():0;s=s||"",i=b.indexOf(" ",i+1),d=b.slice(0,i<0?b.length:i);c.log(s+d);if(i<0){i=0,s+=d}}})()

非高尔夫(chrome):

( a= function (i,s){        // starting index | undefined, output string
    b="( a= "+a+")()";      // get a string representation of the function
    c=console,
    window.onkeydown=function(){    // on each key down event
        c.clear();                  // clear the output 
        s=s||"";
        i=b.indexOf(" ",i+1);       // get the index of next space
        d=b.slice(0,i<0?b.length:i);// get the string part wanted
        c.log(s+d);                 // print the string
        if(i<0){
            i=0,                    // reset counters
            s+=d                    // adding the string to the main output
        }
    }
})()

有两个版本,因为Chrome无法处理箭头功能且未使用相同方法清除控制台

Firefox与Firebug兼容,似乎无法从脚本中清除默认的开发人员控制台。


您是否错过了用户必须按随机键才能打印输出的要求?
Optimizer

当然!,将其重写。
Hacketo 2015年

0

Groovy-379

import java.nio.file.*
Path p = Paths.get(System.getProperty("user.dir"))
DirectoryStream<Path> f = Files.newDirectoryStream(p,"*.groovy")
try{for(e in f){read(e.toAbsolutePath().toString())}}
catch(Exception e){ }
finally{f.close()}

void read(String x){
    def s = new File(x).text
    for(e in s.replace("%n"," %n").split(" ")) 
        print e + " " 
    Thread.sleep(200)
}   

由于没有getch()Java或类似Groovy的Java风格的语言,所以基本上没有代码。就这样:D


0

C,248个字符

真奎因

仅适用于Unix,在Windows中,它将使用_getch实现。

main(){char *p="main(){char *p=\"%s\",s[400];sprintf(s,p,p);system(\"stty raw\");for(p=s;*p!=0;putchar(*p++))getchar();system(\"stty cooked\");}",s[400];sprintf(s,p,p);system("stty raw");for(p=s;*p!=0;putchar(*p++))getchar();system("stty cooked");}

0

HTML和Javascript,232字节

<body><script>var n=0;var f=function (){document.onkeypress=function(){document.body.innerHTML+=("&lt;body>&lt;script>var n=0;var f="+f.toString()+"f()&lt;/script>&lt;/body>").split(" ")[n]+" ";n++;}};f()</script></body>

传统的Javascript quine,但已修改。

JSFiddle 在这里


0

SmileBASIC,79 75字节

LOAD"PRG1:"+PRGNAME$()
PRGEDIT 1
@L
IF BUTTON(2)THEN?PRGGET$();
WAIT
GOTO@L

在SmileBASIC中获得程序的特定LINE非常容易,因此我只在每个换行符之前放置空格。我以为我很聪明,可以在每个换行符前放置空格,但是显然我们可以使用换行符代替空格...

说明:

LOAD "PRG1:"+PRGNAME$() 'load the code into slot 1 so we can easily read 1 line at a time
PRGEDIT 1 'Edit slot 1
@LOOP
IF BUTTON(2) THEN 'When a button is pressed...
                   PRINT PRGGET$(); 'get a line of code and print it
WAIT 'delay so we don't detect the same press multiple times in a single frame.
GOTO @LOOP 

-1

哈斯克尔

{-# LANGUAGE CPP #-}
main = readFile __FILE__ >>= putStrLn

这只是打印其来源。
Carcigenicate
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.