Code auto-modifiable en permanence

14

Maintenant, nous savons tous que la plupart des langages ont des moyens très simples de «s'auto-modifier». Cependant, que se passerait-il si vous deviez réellement modifier le code et en éditer des parties ... sur le disque?

Votre objectif est de créer un code qui imprime un nombre, puis édite son propre fichier pour remplacer le numéro par le suivant dans la séquence de Fibonacci comme suit:

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

Règles

  1. Vous ne pouvez pas enregistrer le (s) numéro (s) "en dehors" du code. Aucun commentaire, aucun ordre de quitter le script, aucun EOF, etc.
  2. Si votre code fonctionne avec n'importe quel nom de fichier, soustrayez 2 de votre nombre d'octets et inscrivez $BYTESNOW ($ORIGINALBYTES - 2)votre titre. (Les noms de fichiers sont supposés être dans la plage de tout chemin de fichier alphanumérique.)
  3. Votre code doit écrire la sortie dans le fichier seul, sans aucune assistance de tuyauterie externe.
  4. Votre code peut commencer à partir de un ou de zéro. Ça n'a pas d'importance.
NO_BOOT_DEVICE
la source
8
La prochaine fois, veuillez publier votre idée dans le bac à sable à la place et laisser le message là pendant quelques jours pour recevoir des commentaires.
JungHwan Min
2
Est-il autorisé d'appeler le programme en invoquant l'interpréteur du langage de programmation (par exemple perl6 program), ou doit-il inclure la ligne shebang afin qu'il puisse être appelé comme ./program?
smls
1
De plus, si nous ne voulons pas opter pour le bonus de -2 octets, pouvons-nous choisir un nom de fichier à un octet ou doit-il être program, et pouvons-nous supposer qu'il se trouve dans le répertoire de travail actuel?
smls
Peut-il être autorisé à échouer lorsque de grands nombres commencent implicitement la conversion en notation exponentielle?
Patrick Roberts
Pourquoi seulement 2 octets de bonus? La plupart des langues, par exemple. Lua, il est plus facile de simplement faire à la "a"place de arg[0]. Cela ne semble pas en valoir la peine.
ATaco

Réponses:

7

Frapper, 52 47 (49-2) octets

MODIFICATIONS:

  • 5 octets enregistrés, en commençant par 1 au lieu de 0. Merci @Leo!

Golfé

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

Tester

>for i in `seq 10`
> do
> ./fibo
> done
1
1
2
3
5
8
13
21
34
55
Zeppelin
la source
2
Je pense que vous pourriez économiser 1 octet en partant de [1 + 0] au lieu de [-1 + 1] (voir 4ème règle du défi)
Leo
2
En fait, cela vous ferait économiser encore plus d'octets en supprimant le -?du regex. Et puisque vous y êtes, vous pouvez également supprimer le premier groupe de capture :)
Leo
@Leo C'est un bon conseil, merci!
zeppelin
2

Python 2, 118111 octets (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)

Il fonctionne avec n'importe quel nom de fichier valide. Il n'y a pas grand-chose à expliquer ici, le code lui-même est très bavard.

Merci à FlipTack de me le rappeler, ce close()n'est pas obligatoire.

Gurupad Mamadapur
la source
1
Ne pouvez-vous pas simplement utiliser à la f=open(...)place de la withdéclaration?
FlipTack
2

Lot, 81 octets

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

Remarque: la nouvelle ligne de fin est significative. Nécessite que le script soit appelé en utilisant son nom complet, y compris l'extension. La sortie commence à 0.

Étant donné que Batch ne peut pas modifier un fichier de manière réaliste, j'ajoute simplement des lignes supplémentaires à la fin du fichier, afin qu'il sache finalement quel est le prochain numéro à imprimer. L' >>%0emplacement enregistre un octet car je ne peux pas le faire précéder d'un chiffre.

Neil
la source
1

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

C'est assez simple. Tout d'abord, il lit puis enregistre les deux caractères à la position 0x1A dans l'en-tête. J'aurais probablement pu chercher plus profondément pour trouver un endroit plus sûr pour enregistrer les données, mais cela fonctionne pour moi sur ma machine exécutant OSX, compilé avec GCC 4.2ish et je doute qu'il soit très portable. De plus, comme il est basé sur des caractères, il déborde après la 13e itération.

Il donne la sortie:

1
1
2
3
5
8
13
21
34
55
Ahemone
la source
1

Node.js, 152137 octets (139-2)

Séparé par des sauts de ligne pour plus de clarté, ne fait pas partie du nombre d'octets.

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

Explication:

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

Usage:

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

Python 3.6, 96 91 (93-2) octets

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)

coder en dur le nom de fichier permettrait d'économiser 5 octets (88 octets):

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)

Enregistré quelques octets grâce à @Artyer

ovs
la source
1
Que diriez-vous de cela (88 octets)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

utilitaires bash + Unix, 43 octets (45-2)

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

La première fois qu'il est exécuté, il utilise dc pour calculer le 1er nombre de Fibonacci via la formule de Binet. Chaque appel à sed modifie le programme en changeant la chaîne passée à dc; ce changement indique à dc d'ajouter un 1 supplémentaire à l'exposant dans la formule, ce qui lui fait calculer le nombre suivant dans la séquence de Fibonacci à chaque fois.

Tester

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

Pour illustrer comment cela fonctionne, à ce stade, après l'impression du 55, le programme a été modifié pour lire:

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

donc le relancer donne

> ./fib
89

et le programme se lit maintenant:

dc -e9k5v1+2/z1+1+1+1+1+1+1+1+1+1+1+^5v/.5+0k1/p;sed -i s/z/z1+/ $0
Mitchell Spector
la source
J'aime ça ! Bien joué !
zeppelin
@zeppelin Merci - cela évite les problèmes avec la version précédente que nous avions.
Mitchell Spector le
1

SmileBASIC 3, 99 octets (101 -2)

Bonus de -2 octets car il fonctionne avec n'importe quel nom de fichier.

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

Celui-ci fonctionne, et il a en quelque sorte fini par être de la même taille que mon cassé!

escargot_
la source
C'est beaucoup plus court si vous ne faites pas le bonus
12Me21
forcer un nom de fichier spécifique me fait me sentir comme un monstre. Je bat quand même la moitié de ces réponses
snail_
Je pense que ne pas désactiver la boîte de dialogue LOAD est bien pire.
12Me21
Il est en fait plus court si vous le chargez dans l'emplacement 1 et utilisez des PRGEDITcommandes pour remplacer la première ligne (et ajoutez un saut de ligne après A=0B=1) Et vous n'avez pas non plus besoin de A=0la première fois.
12Me21
0

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

(A une nouvelle ligne de fin). Il fonctionne avec n'importe quel nom de fichier valide.

plannapus
la source
0

Perl 6 , 67 62 octets (64-2)

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

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

Empilé, non compressif, 65 (67 - 2) octets

Certains problèmes concernant les entrées-sorties de fichiers ont été corrigés dans la dernière série de validations. Ainsi, non compétitif.

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

Voici un lien vers le github.

Exemple d'exécution

(J'ai omis le chemin réel pour plus de clarté.)

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

Explication

Comment cela fonctionne consiste à prendre une paire de nombres pour commencer la séquence ( 2:>dans ce cas, la plage entière [0, 2), qui est (0 1)), puis à effectuer la transformation de Fibonacci sur eux, comme suit:

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

À chaque exécution, cette transformation est exécutée en haut de la pile. Ensuite, la pile est poussée vers la pile, dupliquée et son premier membre obtenu ( stack:0#). Cet élément est ensuite sorti et est le numéro de Fibonacci souhaité. reprprend ensuite la représentation de la pile et ajoute une nouvelle ligne. Ensuite, le programme est poussé vers la pile et divisé sur les nouvelles lignes. Ensuite, nous prenons le dernier membre (la dernière ligne) et l'ajoutons à la chaîne susmentionnée. Enfin, nous poussons d0(le fichier lui-même; pensez dau signe ollar 0== $0.) Et écrivons dessus.

Conor O'Brien
la source
0

Rubis, 68 octets (70-2)

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

Clojure, 209 204 195 octets

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 octets en basculant pour analyser les nombres comme un long au lieu d'un entier, et en supprimant quelques espaces manqués.

-9 octets en supprimant l'espace entre le deuxième nombre et (let...) (l'espace le plus cher jamais!).

Voir les commentaires du code pré-golfé pour une description.

Testé à nouveau et il ne génère plus d'erreurs de support inégalées. Il fonctionne jusqu'à 7540113804746346429, auquel cas il lève une exception de dépassement d'entier.

Notez également que cela suppose que le code source se trouve dans "./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
Carcigenicate
la source