Codice automodificante permanente


14

Ora, sappiamo tutti che molte lingue hanno modi molto semplici di "auto-modificare" il codice. Tuttavia, cosa succederebbe se dovessi modificare effettivamente il codice e modificarne parti ... su disco?

Il tuo obiettivo è creare un codice che stampa un numero, quindi modifica il suo file per sostituire il numero con quello successivo nella sequenza di Fibonacci in questo modo:

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

Regole

  1. Non è possibile memorizzare i numeri "al di fuori" del codice. Nessun commento, nessun ordine di chiusura dello script, nessun EOF, ecc.
  2. Se il tuo codice funziona con qualsiasi nome di file, sottrai 2 dalla quantità di byte e scrivi $BYTESNOW ($ORIGINALBYTES - 2)nel titolo. (Si presume che i nomi dei file rientrino nell'intervallo di qualsiasi percorso di file alfanumerico.)
  3. Il tuo codice deve scrivere l'output sul file da solo, senza alcuna assistenza di piping esterna.
  4. Il tuo codice può iniziare da uno o zero. Non importa

8
La prossima volta, pubblica invece la tua idea nella Sandbox e lascia lì il messaggio per alcuni giorni per ricevere feedback.
JungHwan Min

2
È consentito chiamare il programma invocando l'interprete del linguaggio di programmazione (ad es. perl6 program) O deve includere la linea shebang in modo che possa essere chiamata come ./program?
smls

1
Inoltre, se non vogliamo ottenere il bonus di -2 byte, possiamo scegliere un nome file a byte singolo o deve essere program, e possiamo supporre che si trovi nella directory di lavoro corrente?
smls

Può essere permesso di fallire quando un numero elevato inizia la conversione implicita in notazione esponenziale?
Patrick Roberts,

Perché solo 2 byte di bonus? La maggior parte delle lingue, ad es. Lua, è più facile fare solo "a"invece di arg[0]. Non ne vale la pena.
ATaco,

Risposte:


7

bash, 52 47 (49-2) byte

Modifiche:

  • 5 byte salvati, iniziando con 1 invece di 0. Grazie @Leo!

golfed

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

Test

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

2
Penso che potresti salvare 1 byte a partire da [1 + 0] invece di [-1 + 1] (vedi la quarta regola della sfida)
Leone

2
In realtà, ciò ti farebbe risparmiare ancora più byte rimuovendo -?dal regex. E dato che ci sei, puoi anche rimuovere il primo gruppo di acquisizione :)
Leo

@Leo È un bel consiglio, grazie!
Zeppelin,

2

Python 2, 118 111 byte (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)

Funziona con qualsiasi nome di file valido. Non c'è molto da spiegare qui, il codice stesso è molto dettagliato.

Grazie a FlipTack per avermelo ricordato, close()non è obbligatorio.


1
Non puoi semplicemente usare al f=open(...)posto withdell'istruzione?
FlipTack

2

Lotto, 81 byte

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

Nota: la nuova riga finale è significativa. Richiede che lo script sia invocato usando il suo nome completo inclusa l'estensione. L'uscita inizia da 0.

Poiché Batch non è in grado di modificare realisticamente un file, aggiungo solo righe extra alla fine del file, quindi alla fine saprà quale sarà il prossimo numero da stampare. Il >>%0posizionamento salva un byte perché non posso precederlo con una cifra.


1

C, 142 byte (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));}

È piuttosto semplice. Prima legge, quindi salva i due caratteri nella posizione 0x1A nell'intestazione. Probabilmente avrei potuto cercare più a fondo per trovare un posto più sicuro per salvare i dati, ma funziona per me sul mio computer con OSX, compilato con GCC 4.2ish e dubito che sia molto portatile. Inoltre, poiché è basato sui caratteri, trabocca dopo la 13a iterazione.

Dà l'output:

1
1
2
3
5
8
13
21
34
55

1

Node.js, 152 137 byte (139-2)

Separato con nuove righe per chiarezza, non parte del conteggio dei byte.

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

Spiegazione:

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

Uso:

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

1

Python 3.6, 96 91 (93-2) byte

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)

hardcoding il nome file salverebbe 5 byte (88 byte):

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)

Alcuni byte salvati grazie a @Artyer


1
Che ne dici di questo (88 byte)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 il

1

bash + utility Unix, 43 byte (45-2)

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

La prima volta che viene eseguito, utilizza dc per calcolare il primo numero di Fibonacci tramite la formula Binet. Ogni chiamata a sed modifica il programma cambiando la stringa passata a dc; questa modifica dice a dc di aggiungere un ulteriore 1 all'esponente nella formula, il che gli fa calcolare ogni volta il numero successivo nella sequenza di Fibonacci.

Test

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

Per illustrare come funziona, a questo punto, dopo che il 55 è stato stampato, il programma è stato modificato per leggere:

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

quindi eseguirlo di nuovo produce

> ./fib
89

e il programma ora legge:

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

Mi piace questo ! Molto bene !
Zeppelin,

@zeppelin Grazie - questo evita i problemi con la versione precedente che avevamo.
Mitchell Spector,

1

SmileBASIC 3, 99 byte (101 -2)

-2 byte bonus perché funziona con qualsiasi nome di file.

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

Questo funziona, e in qualche modo ha finito per avere le stesse dimensioni del mio rotto!


È molto più breve se non fai il bonus
12Me21

forzare un nome di file specifico mi fa sentire un mostro. Sto comunque battendo metà di queste risposte
lumaca_

Penso che non disattivare la finestra di dialogo LOAD sia molto peggio.
12Me21

In realtà è più breve se lo si carica nello slot 1 e si usano i PRGEDITcomandi per sostituire la prima riga (e si aggiunge una interruzione di riga dopo A=0B=1) E inoltre non è necessario A=0la prima volta.
12Me21

0

R, 145 byte (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")

(Ha una nuova riga finale). Funziona con qualsiasi nome di file valido.


0

Perl 6 , 67 62 byte (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

Impilati, non competitivi, 65 (67 - 2) byte

Alcuni problemi relativi al file IO sono stati risolti nella serie più recente di commit. Pertanto, non competitivo.

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

Ecco un link al github.

Esempio di esecuzione

(Ho omesso il percorso effettivo per chiarezza.)

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

Spiegazione

Come funziona prendendo una coppia di numeri per iniziare la sequenza ( 2:>in questo caso è l'intervallo intero [0, 2), ovvero (0 1)), quindi eseguendo la trasformazione di Fibonacci su di essi, in questo modo:

: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)

Ad ogni esecuzione, questa trasformazione viene eseguita in cima allo stack. Quindi, lo stack viene inserito nello stack, duplicato e ottenuto il suo primo membro ( stack:0#). Questo articolo viene quindi emesso ed è il numero di Fibonacci desiderato. reprquindi prende la rappresentazione dello stack e aggiunge una nuova riga. Quindi, il programma viene inserito nello stack e suddiviso su nuove righe. Quindi prendiamo l'ultimo membro (l'ultima riga) e lo aggiungiamo alla stringa di cui sopra. Infine, spingiamo d0(il file stesso; pensa dollar sign 0== $0.) E scriviamo ad esso.


0

Rubino, 68 byte (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 byte

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 byte passando a analizzare i numeri come un lungo anziché un intero e rimuovendo un paio di spazi mancanti.

-9 byte rimuovendo lo spazio tra il secondo numero e (let...)(lo spazio più costoso di sempre!).

Vedere i commenti sul codice pregolfed per una descrizione.

Testato di nuovo e non genera più errori di parentesi senza pari. Funziona fino a 7540113804746346429, a quel punto genera un'eccezione di overflow del numero intero.

Si noti inoltre che ciò presuppone che il codice sorgente si trovi in ​​"./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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.