永久自修改代码


14

现在,我们都知道大多数语言都有非常简单的方法来“自我修改”代码。但是,如果要在磁盘上实际修改代码并编辑其中的一部分,该怎么办?

您的目标是制作打印数字的代码,然后编辑自己的文件,用斐波那契数列中的下一个替换该数字,如下所示:

$ ./program
1
$ ./program
1
$ ./program
2
$ ./program
3
$ ./program
5
[etc...]

规则

  1. 您不能将代码存储在代码的“外部”。没有评论,没有告诉脚本退出,没有EOF等。
  2. 如果您的代码可以使用任何文件名,请从字节数中减去2并$BYTESNOW ($ORIGINALBYTES - 2)输入标题。(假定文件名在任何字母数字文件路径的范围内。)
  3. 您的代码必须自己将输出写入文件,而无需任何外部管道协助。
  4. 您的代码可以从一或零开始。没关系

8
下次,请将您的想法发布在沙盒中,然后将其保留几天以接收反馈。
JungHwan Min

2
是否可以通过调用编程语言的解释程序(例如perl6 program)来调用程序,还是必须包含shebang行才能将其称为./program
smls

1
另外,如果我们不想获得-2字节的奖励,是否可以选择单字节文件名还是必须为program,并且可以假定它位于当前工作目录中?
smss

当大量数字开始隐式转换为指数表示法时,是否可以允许它失败?
Patrick Roberts

为什么只有2个字节的奖金?大多数语言,例如。Lua中,有它会更容易些"a",而不是arg[0]。这似乎不值得。
ATaco

Answers:


7

重击 52 47(49-2)字节

编辑:

  • 通过从1而不是0开头节省了5个字节。谢谢@Leo!

打高尔夫球

A=$[1+0]
echo $A
sed -ri "s/\w+\+(\w+)/\1+$A/" $0

测试

>for i in `seq 10`
> do
> ./fibo
> done
1
1
2
3
5
8
13
21
34
55

2
我认为您可以从[1 + 0]开始而不是[-1 + 1]来节省1个字节(请参阅挑战的第4条规则)
Leo

2
实际上,通过-?从正则表达式中删除,可以使您节省更多的字节。而且由于您在那,所以您也可以删除第一个捕获组:)
Leo

@Leo这是一个很好的建议,谢谢!
Zeppelin

2

Python 2中,118个 111字节(113 - 2)

a,b=0,1;print a
f=open(__file__,'r+')
s=f.read()
s=s.replace(s[4:s.find(';')],`b`+','+`a+b`)
f.seek(0)
f.write(s)

它适用于任何有效的文件名。这里没有太多要解释的内容,代码本身非常冗长。

感谢FlipTack提醒我,close()这不是强制性的。


1
您不能只使用f=open(...)而不是with语句吗?
FlipTack

2

批次,81个字节

@call:c
@set/az=x+y
@echo %x%
@echo>>%0 @set/ax=%z%,y=%x%
:c
@set/ax=0,y=1

注意:尾随换行符很重要。要求使用脚本的全名(包括扩展名)调用脚本。输出从0开始。

由于Batch无法实际编辑文件,因此我只在文件末尾添加了额外的行,因此最终它会知道要打印的下一个数字。该>>%0位置节省了一个字节,因为我不能在其前面加上数字。


1

C,142字节(144-2)

void main(int x,char**a){FILE*f=fopen(*a,"r+");fseek(f,27,0);char n=fgetc(f),m=fgetc(f);fseek(f,27,0);printf("%d\n",fputc(fputc(m,f)?n+m:1,f));}

非常简单。首先读取,然后将两个字符保存在标头中的0x1A位置。我也许可以更深入地寻找一个更安全的位置来保存数据,但是它在运行OSX(使用GCC 4.2ish编译)的计算机上适用于我,我怀疑它是否可移植。另外,由于它基于字符,因此在第13次迭代后会溢出。

它给出了输出:

1
1
2
3
5
8
13
21
34
55

1

Node.js,152 137字节(139 - 2)

为了清楚起见,用换行符分隔,而不是字节计数的一部分。

f=_=>require('fs').writeFileSync(__filename,
`f=${f};f()`.replace(/(\d[^,]*),(\d[^\)]*)/,
(m,a,b)=>`${b=+b},${+a+b}`),console.log((0,1)));
f()

说明:

f=_=>                          // define `f` as function with a single unused argument `_`
  require('fs').writeFileSync( // import the standard filesystem module and overwrite file
    __filename,                // string var containing path of file for current module
    `f=${f};f()`.replace(      // template string containing source of entire script
      /(\d[^,]*),(\d[^\)]*)/,  // regexp to match and group the numbers in this script
      (m,a,b)=>                // replace function with arguments match, group a, group b
        `${b=+b},${+a+b}`      // template string incrementing fibonacci numbers in place
    ),                         // end replace()
    console.log(               // prints to stdout, `undefined` passed to argument
      (0,1)                    // comma separated group returns value of last expression
    )                          // end console.log()
  )                            // end fs.writeFileSync()
;                              // end statement defining `f` as arrow function
f()                            // run function to modify script and print fibonacci number

用法:

// assuming above script is stored in program.js
$ node program
1
$ node program
1
$ node program
2
$ node program
3
$ node program
5
...

1

蟒3.6,96 91(93-2)个字节

a,b=0,1
f=open(__file__,"r+");next(f);f.write(f"a,b={b,a+b}\n{next(f)}{f.seek(0)}");print(b)

对文件名进行硬编码将节省5个字节(88个字节):

a,b=0,1
f=open("f","r+");next(f);f.write(f"a,b={b,a+b}\n{next(f)}{f.seek(0)}");print(b)

@Artyer节省了一些字节


1
怎么样(88字节)a,b=0,1 f=open('f','r+');next(f);f.write(f'a,b={b,a+b}\n{next(f)}{f.seek(0)}');print(b)#
Artyer

1

bash + Unix实用程序,43字节(45-2)

dc -e9k5v1+2/z^5v/.5+0k1/p;sed -i s/z/z1+/ $0

第一次运行时,它使用dc通过Binet公式计算第一个斐波那契数。每次对sed的调用都会通过更改传递给dc的字符串来修改程序。此更改使dc向公式中的指数添加一个额外的1,这使它每次都计算Fibonacci序列中的下一个数字。

测试

> for k in {1..10}
> do
> ./fib
> done
1
1
2
3
5
8
13
21
34
55

为了说明它是如何工作的,现在,在打印55之后,该程序已修改为:

dc -e9k5v1+2/z1+1+1+1+1+1+1+1+1+1+^5v/.5+0k1/p;sed -i s/z/z1+/ $0

所以再次运行它会产生

> ./fib
89

该程序现在显示为:

dc -e9k5v1+2/z1+1+1+1+1+1+1+1+1+1+1+^5v/.5+0k1/p;sed -i s/z/z1+/ $0

我喜欢这个 !做得好 !
齐柏林飞艇

@zeppelin谢谢您-避免了我们以前版本的问题。
米切尔·史派克特

1

SmileBASIC 3,99个字节(101 -2)

-2字节奖励,因为它可与任何文件名一起使用。

A=0B=1F$="TXT:"+PRGNAME$()S$=LOAD(F$)SAVE F$,SUBST$(S$,0,INSTR(S$,"F"),FORMAT$("A=%DB=%D",B,A+B))?A+B

这个确实可以用,但最终却和我坏掉的那个一样大!


如果您不
支付

强制使用特定的文件名会让我感到异常。无论如何,我还是打败了这些答案的一半
snail_

我认为不关闭LOAD对话框会更糟。
17Me21年

如果将其加载到插槽1中并使用PRGEDIT命令替换第一行(并在之后添加换行符A=0B=1),则实际上会更短,而且您也不需要A=0第一次。
17Me21年

0

R,145字节(147-2)

a=c(1,1)
cat(a[1])
R=readLines(f<-sub("^.+=","",grep("^--f",commandArgs(F),v=T)))
cat(c(sprintf("a=c(%i,%i)",a[2],sum(a)),R[-1]),file=f,sep="\n")

(具有结尾的换行符)。它适用于任何有效的文件名。


0

Perl 6的67 62个字节(64 - 2)

say (1,1,*+*...*)[1];$*PROGRAM.&{.spurt: .slurp.&{S/\[<(\d+/{$/+1}/}}

say 0+1;$*PROGRAM.&{.spurt: .slurp.&{S/(\d+).(\d+)/$1+{$0+$1}/}}

0

堆叠式非竞争,65(67-2)字节

有关文件IO的一些问题已在最近的一系列提交中修复。因此,不竞争。

2:>
:sum\tail...\stack:0#out repr LF+program LF split last+d0\write

这是指向github的链接。

执行示例

(为清楚起见,我省略了实际路径。)

C:\
λ type permanently-self-modifying-code.stk
2:>
:sum\last\stack:0#out repr LF+program LF split last+d0\write
C:\
λ stacked permanently-self-modifying-code.stk
1

C:\
λ stacked permanently-self-modifying-code.stk
1

C:\
λ stacked permanently-self-modifying-code.stk
2

C:\
λ stacked permanently-self-modifying-code.stk
3

C:\
λ stacked permanently-self-modifying-code.stk
5

C:\
λ stacked permanently-self-modifying-code.stk
8

说明

这是如何工作的,方法是采用一对数字开始序列(2:>在本例中为整数range [0, 2),即(0 1)),然后对它们执行Fibonacci转换,如下所示:

:sum\last\                     top of stack: (x y)
:              duplicate.             stack: ((x y) (x y))
 sum           sum of TOs.            stack: ((x y) x+y)
    \          swap order.            stack: (x+y (x y))
     last      obtain last element.   stack: (x+y y)
         \     swap order.            stack: (y x+y)

每次运行时,此转换都在堆栈的顶部执行。然后,将堆栈推入堆栈,进行复制,并获得其第一个成员(stack:0#)。然后输出该项目,它是所需的斐波那契数。repr然后获取堆栈的表示形式并追加换行符。然后,将程序推入堆栈,并在换行符上分割。然后,我们获取最后一个成员(最后一行),并将其附加到上述字符串中。最后,我们推送d0(文件本身;认为是dollar sign 0== $0)并写入该文件。


0

Ruby,68个字节(70-2)

p$a=1+0
f=open$0,'r+'
s=f.read.sub /\d+.(\d+)/,"\\1+#$a"
f.seek 0
f<<s

0

Clojure,209 204 195字节

0 1(let[u #(apply str %)a"./src/s.clj"p #(Long/parseLong(u %))l(fn[v](split-with #(Character/isDigit %)v))c(slurp a)[n[_ & r]](l c)[m r](l r)b(+(p n)(p m))](println b)(spit a(str(p m)" "b(u r))))

通过切换以将数字解析为长整数而不是整数来解析-5字节,并删除几个丢失的空格。

删除第二个数字和 (let...)(有史以来最昂贵的空间)。

有关说明,请参见预先编写的代码注释。

经过重新测试,它不再引发无与伦比的括号错误。它可以工作到7540113804746346429,这时将引发整数溢出异常。

另请注意,这假定源代码位于“ ./src/s.clj”。

0 1 ; Starting numbers
(let [; The first 4 entires are shortcuts to functions and data that are used more than once
      u #(apply str %) ; Turns a list into a string
      a "./src/s.clj" ; Current location
      p #(Integer/parseInt (u %)) ; Integer parsing shortcut
      ; Used to split a string on digits to parse them out
      l (fn [v] (split-with #(Character/isDigit %) v))
      src (slurp a) ; Get the source
      [n [_ & r]] (l src) ; Use deconstructuring to grab the first number
      [m r] (l r) ; Same as above, grabbing the second number
      n' (+ (p n) (p m)) ; Parse the 2 numbers, and add them
      ; Put everything back together, only this time with the new numbers
      k (str (p m) " " n' (u r))]
  (println n') ; Print the new number
  (spit a k)) ; Overwrite the old source
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.