Código de auto modificación permanente


14

Ahora, todos sabemos que la mayoría de los idiomas tienen formas muy simples de "auto modificar" el código. Sin embargo, ¿qué pasaría si realmente modificara el código y editara partes de él ... en el disco?

Su objetivo es crear un código que imprima un número, luego edite su propio archivo para reemplazar el número con el siguiente en la secuencia de Fibonacci de la siguiente manera:

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

Reglas

  1. No puede almacenar los números "fuera" del código. Sin comentarios, sin decirle al script que salga, sin EOF, etc.
  2. Si su código funciona con cualquier nombre de archivo, reste 2 de la cantidad de bytes y escriba $BYTESNOW ($ORIGINALBYTES - 2)su título. (Se supone que los nombres de archivo están dentro del rango de cualquier ruta de archivo alfanumérica).
  3. Su código debe escribir la salida en el archivo por sí mismo, sin ninguna ayuda de tubería externa.
  4. Su código puede comenzar desde uno o cero. No importa.

8
La próxima vez, publique su idea en el Sandbox y deje la publicación allí durante unos días para recibir comentarios.
JungHwan Min

2
¿Está permitido llamar al programa invocando al intérprete del lenguaje de programación (por ejemplo perl6 program), o tiene que incluir la línea shebang para que pueda llamarse como ./program?
sonríe el

1
Además, si no queremos obtener el bono de -2 bytes, ¿podemos elegir un nombre de archivo de un solo byte o debe ser así program, y podemos suponer que se encuentra en el directorio de trabajo actual?
sonríe el

¿Se puede permitir que falle cuando grandes números comienzan implícitamente a convertirse en notación exponencial?
Patrick Roberts

¿Por qué solo 2 bytes de bonificación? La mayoría de los idiomas, por ej. Lua, que sea más fácil hacerlo en "a"lugar de hacerlo arg[0]. No parece que valga la pena.
ATaco

Respuestas:


7

Golpetazo, 52 47 (49-2) bytes

EDICIONES:

  • Guardado 5 bytes, comenzando con 1 en lugar de 0. ¡Gracias @Leo!

Golfed

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

Prueba

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

2
Creo que podría guardar 1 byte comenzando desde [1 + 0] en lugar de [-1 + 1] (vea la cuarta regla del desafío)
Leo

2
En realidad, eso te haría ahorrar aún más bytes al eliminar el -?de la expresión regular. Y como estás allí, también puedes eliminar el primer grupo de captura :)
Leo

@Leo Ese es un buen consejo, ¡gracias!
zeppelin

2

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

Funciona con cualquier nombre de archivo válido. No hay mucho que explicar aquí, el código en sí es muy detallado.

Gracias a FlipTack por recordármelo, close()no es obligatorio.


1
¿No puedes usar en f=open(...)lugar de la withdeclaración?
FlipTack

2

Lote, 81 bytes

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

Nota: la nueva línea final es significativa. Requiere que se invoque el script utilizando su nombre completo, incluida la extensión. La salida comienza en 0.

Como Batch no puede editar un archivo de manera realista, solo agrego líneas adicionales al final del archivo, para que eventualmente sepa cuál es el siguiente número para imprimir. La >>%0ubicación guarda un byte porque no puedo precederlo con un dígito.


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 bastante sencillo. Primero lee y luego guarda los dos caracteres en la posición 0x1A en el encabezado. Probablemente podría haber buscado más profundamente para encontrar un lugar más seguro para guardar los datos, pero me funciona en mi máquina con OSX, compilada con GCC 4.2ish y dudo que sea muy portátil. Además, dado que se basa en caracteres, se desborda después de la 13ª iteración.

Da la salida:

1
1
2
3
5
8
13
21
34
55

1

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

Separado con líneas nuevas para mayor claridad, no forma parte del recuento de bytes.

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

Explicación:

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

codificar el nombre del archivo ahorraría 5 bytes (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)

Guardado algunos bytes gracias a @Artyer


1
¿Qué tal esto? (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 + utilidades Unix, 43 bytes (45-2)

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

La primera vez que se ejecuta esto, usa dc para calcular el primer número de Fibonacci a través de la fórmula de Binet. Cada llamada a sed modifica el programa cambiando la cadena pasada a dc; este cambio le dice a dc que agregue un 1 adicional al exponente en la fórmula, lo que hace que calcule el siguiente número en la secuencia de Fibonacci cada vez.

Prueba

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

Para ilustrar cómo funciona, en este punto, después de que se imprime el 55, el programa se ha modificado para que lea:

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

así que ejecutarlo nuevamente produce

> ./fib
89

y el programa ahora lee:

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

Me gusta esto ! Bien hecho !
zepelín

@zeppelin Gracias, esto evita los problemas con la versión anterior que teníamos.
Mitchell Spector

1

SmileBASIC 3, 99 bytes (101 -2)

Bono de -2 bytes porque funciona con cualquier nombre de archivo.

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

¡Este sí funciona, y de alguna manera terminó siendo del mismo tamaño que mi roto!


Es mucho más corto si no haces la bonificación
12Me21

forzar un nombre de archivo específico me hace sentir como un bicho raro. Estoy batiendo la mitad de estas respuestas de todos modos
snail_

Creo que no desactivar el diálogo CARGAR es mucho peor.
12Me21

En realidad, es más corto si lo carga en la ranura 1 y usa PRGEDITcomandos para reemplazar la primera línea (y agrega un salto de línea después A=0B=1) Y tampoco necesita A=0la primera vez.
12Me21

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

(Tiene una nueva línea al final). Funciona con cualquier nombre de archivo válido.


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

Apilados, no competitivos, 65 (67 - 2) bytes

Algunos problemas relacionados con el archivo IO se solucionaron en la serie más reciente de confirmaciones. Por lo tanto, sin competencia.

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

Aquí hay un enlace al github.

Ejecución de ejemplo

(Omití el camino real para mayor claridad).

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

Explicación

Cómo funciona esto es tomando un par de números para comenzar la secuencia ( 2:>en este caso es el rango entero [0, 2), que es (0 1)), y luego realizando la transformación de Fibonacci en ellos, así:

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

En cada ejecución, esta transformación se ejecuta en la parte superior de la pila. Luego, la pila se empuja a la pila, se duplica y se obtiene su primer miembro ( stack:0#). Este elemento se emite y es el número de Fibonacci deseado. reprluego toma la representación de la pila y agrega una nueva línea. Luego, el programa se empuja a la pila y se divide en nuevas líneas. Luego, tomamos el último miembro (la última línea) y lo agregamos a la cadena mencionada anteriormente. Finalmente, empujamos d0(el archivo en sí; piense en dollar sign 0==$0 .) Y le escribimos.


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 cambiando para analizar los números como largos en lugar de enteros, y eliminando un par de espacios perdidos.

-9 bytes eliminando el espacio entre el segundo número y (let...) (¡el espacio más caro de la historia!).

Vea los comentarios del código pregolfed para una descripción.

Probado de nuevo, y ya no arroja errores de paréntesis sin igual. Funciona hasta 7540113804746346429, momento en el que arroja una excepción de desbordamiento de entero.

También tenga en cuenta que esto supone que el código fuente se encuentra en "./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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.