Dauerhaft selbstmodifizierender Code


14

Nun wissen wir alle, dass die meisten Sprachen sehr einfache Möglichkeiten haben, Code selbst zu modifizieren. Was wäre jedoch, wenn Sie den Code tatsächlich ändern und Teile davon bearbeiten würden ... auf der Festplatte?

Ihr Ziel ist es, einen Code zu erstellen, der eine Zahl druckt, und dann eine eigene Datei zu bearbeiten, um die Zahl wie folgt durch die nächste in der Fibonacci-Sequenz zu ersetzen:

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

Regeln

  1. Sie dürfen die Nummer (n) nicht "außerhalb" des Codes speichern. Keine Kommentare, kein Verlassen des Skripts, kein EOF usw.
  2. Wenn Ihr Code mit einem Dateinamen funktioniert, subtrahieren Sie 2 von Ihrer Bytemenge und schreiben Sie $BYTESNOW ($ORIGINALBYTES - 2)Ihren Titel ein. (Es wird davon ausgegangen, dass Dateinamen im Bereich eines beliebigen alphanumerischen Dateipfads liegen.)
  3. Ihr Code muss die Ausgabe ohne externe Piping-Unterstützung in die Datei schreiben.
  4. Ihr Code kann bei eins oder null beginnen. Es spielt keine Rolle.

8
Wenn Sie das nächste Mal Ihre Idee in der Sandbox veröffentlichen, lassen Sie sie dort einige Tage, um Feedback zu erhalten.
JungHwan Min

2
Darf das Programm durch Aufrufen des Interpreters der Programmiersprache (zB perl6 program) aufgerufen werden, oder muss die shebang-Zeile enthalten sein, damit es aufgerufen werden kann als ./program?
smls

1
Wenn wir uns nicht für den -2-Byte-Bonus entscheiden möchten, können wir dann einen Einzelbyte-Dateinamen auswählen oder müssen wir ihn auswählen programund können wir davon ausgehen, dass er sich im aktuellen Arbeitsverzeichnis befindet?
smls

Kann es fehlschlagen, wenn große Zahlen implizit in Exponentialschreibweise konvertiert werden?
Patrick Roberts

Warum nur 2 Bytes Bonus? Die meisten Sprachen, z. Lua, es ist einfacher, es einfach zu machen, "a"anstatt arg[0]. Es scheint es nicht wert zu sein.
ATaco

Antworten:


7

Bash, 52 47 (49-2) Bytes

EDITS:

  • 5 Bytes gespeichert, beginnend mit 1 anstelle von 0. Danke @Leo!

Golf gespielt

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

Prüfung

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

2
Ich denke, Sie könnten 1 Byte sparen, indem Sie mit [1 + 0] anstelle von [-1 + 1] beginnen (siehe 4. Regel der Herausforderung)
Leo

2
Tatsächlich würden Sie dadurch noch mehr Bytes einsparen, indem Sie das -?aus dem regulären Ausdruck entfernen . Und da Sie dort sind, können Sie auch die erste Erfassungsgruppe entfernen :)
Leo

@Leo Das ist ein netter Rat, danke!
Zeppelin

2

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

Es funktioniert mit jedem gültigen Dateinamen. Es gibt hier nicht viel zu erklären, der Code selbst ist sehr ausführlich.

Vielen Dank an FlipTack für die Erinnerung, close()ist nicht zwingend erforderlich.


1
Kannst du nicht einfach f=open(...)anstelle der withAussage verwenden?
FlipTack

2

Batch, 81 Bytes

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

Hinweis: Die nachfolgende Newline ist signifikant. Erfordert, dass das Skript mit seinem vollständigen Namen einschließlich der Erweiterung aufgerufen wird. Die Ausgabe beginnt bei 0.

Da Batch eine Datei nicht realistisch bearbeiten kann, füge ich einfach zusätzliche Zeilen am Ende der Datei hinzu, damit sie schließlich weiß, welche Nummer als nächste gedruckt werden soll. Durch die >>%0Platzierung wird ein Byte gespeichert, da ich keine Ziffer voranstellen kann.


1

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

Es ist ziemlich einfach. Zuerst liest es, dann speichert es die beiden Zeichen an der Position 0x1A in der Kopfzeile. Ich hätte wahrscheinlich genauer hinsehen können, um einen sichereren Ort zum Speichern der Daten zu finden, aber es funktioniert auf meinem Computer mit OSX, der mit GCC 4.2ish kompiliert wurde, und ich bezweifle, dass es sehr portabel ist. Da es auf Zeichen basiert, läuft es auch nach der 13. Iteration über.

Es gibt die Ausgabe:

1
1
2
3
5
8
13
21
34
55

1

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

Aus Gründen der Übersichtlichkeit mit Zeilenumbrüchen getrennt, nicht Teil der Byteanzahl.

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

Erläuterung:

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

Verwendung:

// 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) Bytes

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)

Durch Hardcodierung des Dateinamens würden 5 Bytes (88 Bytes) gespart:

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)

Einige Bytes dank @Artyer eingespart


1
Wie wäre es damit (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-Dienstprogramme, 43 Bytes (45-2)

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

Bei der ersten Ausführung wird dc verwendet, um die 1. Fibonacci-Zahl über die Binet-Formel zu berechnen. Jeder Aufruf von sed ändert das Programm, indem der an dc übergebene String geändert wird. Diese Änderung weist dc an, dem Exponenten in der Formel eine zusätzliche 1 hinzuzufügen, wodurch die nächste Zahl in der Fibonacci-Sequenz jedes Mal berechnet wird.

Prüfung

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

Um zu veranschaulichen, wie es funktioniert, wurde das Programm zu diesem Zeitpunkt nach dem Drucken der 55 folgendermaßen geändert:

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

So läuft es wieder ergibt

> ./fib
89

und das programm lautet nun:

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

Ich mag das ! Gut gemacht !
Zeppelin

@zeppelin Danke - das vermeidet die Probleme mit der vorherigen Version, die wir hatten.
Mitchell Spector

1

SmileBASIC 3, 99 Bytes (101 -2)

-2-Byte-Bonus, weil es mit jedem Dateinamen funktioniert.

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

Dieser hat funktioniert, und es endete irgendwie in der gleichen Größe wie meine kaputte!


Es ist viel kürzer, wenn Sie den Bonus nicht tun
12Me21

Wenn ich einen bestimmten Dateinamen erzwinge, fühle ich mich wie ein Freak. Ich
schlage

Ich denke, dass es viel schlimmer ist, den LOAD-Dialog nicht auszuschalten.
Mai, 21.

Es ist tatsächlich kürzer, wenn Sie es in Steckplatz 1 laden und PRGEDITBefehle verwenden, um die erste Zeile zu ersetzen (und danach einen Zeilenumbruch einzufügen A=0B=1). Außerdem müssen Sie A=0das erste Mal nicht.
Mai, 21.

0

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

(Hat einen abschließenden Zeilenumbruch). Es funktioniert mit jedem gültigen Dateinamen.


0

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

Gestapelt, nicht konkurrierend, 65 (67 - 2) Bytes

Einige Probleme in Bezug auf Datei-E / A wurden in der letzten Reihe von Festschreibungen behoben. Somit konkurrenzlos.

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

Hier ist ein Link zum Github.

Beispielausführung

(Der Übersichtlichkeit halber habe ich den tatsächlichen Pfad weggelassen.)

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

Erläuterung

Das funktioniert, indem Sie ein Zahlenpaar nehmen, um die Sequenz zu beginnen ( 2:>in diesem Fall ist das der ganzzahlige Bereich ), und dann die Fibonacci-Transformation für sie durchführen, wie [0, 2)folgt (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)

Bei jedem Durchlauf wird diese Transformation oben im Stapel ausgeführt. Dann wird der Stapel auf den Stapel geschoben, dupliziert und sein erstes Element erhalten ( stack:0#). Dieser Punkt wird dann ausgegeben und ist die gewünschte Fibonacci-Nummer. reprNimmt dann die Darstellung des Stapels und fügt eine neue Zeile hinzu. Anschließend wird das Programm in den Stapel verschoben und in Zeilenumbrüche aufgeteilt. Dann nehmen wir das letzte Element (die letzte Zeile) und hängen es an die oben genannte Zeichenfolge an. Zum Schluss pushen wir d0(die Datei selbst; denke dollar sign 0== $0.) Und schreiben darauf.


0

Ruby, 68 Bytes (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 Bytes

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 Bytes durch Umschalten, um die Zahlen als Long statt als Integer zu analysieren und einige fehlende Leerzeichen zu entfernen.

-9 Bytes durch Entfernen des Leerzeichens zwischen der zweiten Zahl und (let...)(der teuerste Speicherplatz aller Zeiten!).

Eine Beschreibung finden Sie in den Kommentaren zum vorbereiteten Code.

Erneut getestet, und es werden keine unübertroffenen Klammerfehler mehr ausgegeben. Es funktioniert bis zu 7540113804746346429 und löst an diesem Punkt eine Ganzzahlüberlaufausnahme aus.

Beachten Sie auch, dass sich der Quellcode unter "./src/s.clj" befindet.

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.