영구적으로 자체 수정 코드


14

이제 우리는 대부분의 언어가 코드를 "자체 수정"하는 매우 간단한 방법을 가지고 있음을 알고 있습니다. 그러나 실제로 코드를 수정하고 디스크의 일부를 편집하려면 어떻게해야합니까?

목표는 숫자를 인쇄하는 코드를 만든 다음 자체 파일을 편집하여 숫자를 피보나치 시퀀스의 다음 숫자로 바꿉니다.

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

규칙

  1. 코드의 "외부"번호는 저장할 수 없습니다. 주석 없음, 스크립트 종료를 알리지 않음, EOF 등 없음
  2. 코드가 파일 이름으로 작동하면 바이트 수에서 2를 빼고 $BYTESNOW ($ORIGINALBYTES - 2)제목을 쓰 십시오. (파일 이름은 영숫자 파일 경로 범위 내에있는 것으로 가정합니다.)
  3. 코드는 외부 배관 지원없이 파일에 출력을 파일에 직접 써야합니다.
  4. 코드는 1 또는 0에서 시작할 수 있습니다. 중요하지 않습니다.

8
다음에 아이디어를 샌드 박스 에 게시하고 며칠 동안 게시물을 남겨 피드백을 받도록하세요.
JungHwan Min

2
프로그래밍 언어의 인터프리터 (예 perl6 program:) 를 호출하여 프로그램을 호출 할 수 ./program있습니까 , 아니면 호출 될 수 있도록 shebang 행을 포함해야 합니까?
smls

1
또한 -2 바이트 보너스를 원치 않으면 1 바이트 파일 이름을 선택하거나 파일 이름을 선택해야 program하며 현재 작업 디렉토리에 있다고 가정 할 수 있습니까?
smls

숫자가 암시 적으로 지수 표기법으로 변환되기 시작할 때 실패 할 수 있습니까?
Patrick Roberts

왜 2 바이트 만 보너스입니까? 대부분의 언어, 예. 루아, "a"대신 쉽게 할 수 있습니다 arg[0]. 그만한 가치가 없어 보입니다.
ATaco

답변:


7

세게 때리다, 52 47 (49-2) 바이트

EDITS :

  • 0 대신 1로 시작하여 5 바이트를 절약했습니다. Thanks @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 + 1] 대신 [1 + 0]부터 시작하여 1 바이트를 절약 할 수 있다고 생각합니다 (4 번째 규칙 참조)
Leo

2
실제로, -?정규식에서 를 제거하여 더 많은 바이트를 절약 할 수 있습니다 . 그리고 당신이 거기에 있기 때문에, 당신은 또한 첫번째 캡처 그룹을 제거 할 수 있습니다 :)
Leo

@ 레오 좋은 조언입니다, 감사합니다!
zeppelin

2

파이썬 2, 118111 바이트 (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 위치에 두 문자를 읽고 저장합니다. 데이터를 저장하기에 안전한 장소를 찾기 위해 더 깊이 살펴 보았을 수도 있지만 GCC 4.2ish로 컴파일 된 OSX를 실행하는 내 컴퓨터에서 작동하며 이식성이 뛰어납니다. 또한 문자를 기반으로하기 때문에 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 bytes)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을 더 추가하여 매번 피보나치 시퀀스의 다음 숫자를 계산합니다.

테스트

> 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

@zeppelin 감사합니다. 이렇게하면 이전 버전의 문제를 피할 수 있습니다.
Mitchell Spector

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

이것은 작동하지만 어쨌든 내 깨진 것과 같은 크기가되었습니다!


보너스를하지 않으면 훨씬 짧습니다
12Me21

특정 파일 이름을 강요하면 괴물처럼 느껴집니다. 어쨌든 나는이 답변의 절반을 치고 있어요
달팽이 _

LOAD 대화 상자를 끄지 않는 것이 훨씬 나쁘다고 생각합니다.
12Me21

슬롯 1에로드하고 PRGEDIT명령을 사용 하여 첫 번째 줄을 바꾸고 뒤에 줄 바꿈을 추가 하면 실제로 더 짧 습니다. A=0B=1또한 A=0처음으로 필요하지 않습니다 .
12Me21

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:>이 경우 정수 범위 [0, 2)이며, (0 1)다음과 같이, 그들을 피보나치 변환을 수행하는)

: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

루비, 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 바이트.

두 번째 숫자와 사이의 공백을 제거하여 -9 바이트 (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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.