Código permanentemente auto-modificável


14

Agora, todos sabemos que a maioria dos idiomas possui maneiras muito simples de "auto-modificar" o código. No entanto, e se você realmente modificar o código e editar partes dele ... no disco?

Seu objetivo é criar um código que imprima um número e, em seguida, edite seu próprio arquivo para substituir o número pelo próximo na sequência de Fibonacci, da seguinte maneira:

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

Regras

  1. Você não pode armazenar os números "fora" do código. Sem comentários, sem dizer ao script para sair, sem EOF, etc.
  2. Se o seu código funcionar com qualquer nome de arquivo, subtraia 2 da quantidade de bytes e escreva $BYTESNOW ($ORIGINALBYTES - 2)no título. (Presume-se que os nomes de arquivos estejam dentro do intervalo de qualquer caminho alfanumérico do arquivo.)
  3. Seu código deve gravar a saída no arquivo por si próprio, sem qualquer assistência de tubulação externa.
  4. Seu código pode começar de um ou zero. Não importa.

8
Da próxima vez, publique sua ideia na Sandbox e deixe a postagem lá por alguns dias para receber feedback.
JungHwan Min

2
É permitido chamar o programa chamando o intérprete da linguagem de programação (por exemplo perl6 program) ou precisa incluir a linha shebang para que possa ser chamada como ./program?
SMLS

1
Além disso, se não quisermos obter o bônus de -2 bytes, podemos escolher um nome de arquivo de um byte ou precisa ser program, e podemos assumir que ele está localizado no diretório de trabalho atual?
SMLS

Pode-se falhar quando grandes números começam a ser convertidos implicitamente em notação exponencial?
Patrick Roberts

Por que apenas 2 bytes de bônus? A maioria dos idiomas, por exemplo. Lua, tê-lo mais fácil apenas para fazer "a"em vez de arg[0]. Não parece valer a pena.
ATaco 22/01

Respostas:


7

Bater, 52 47 (49-2) bytes

EDITAR% S:

  • Economizou 5 bytes, começando com 1 em vez de 0. Obrigado @Leo!

Golfe

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

Teste

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

2
Eu acho que você poderia salvar 1 byte pela partir de [1 + 0] em vez de [-1 + 1] (ver 4ª regra do desafio)
Leo

2
Na verdade, isso faria você economizar ainda mais bytes removendo o -?da expressão regular. E uma vez que você está lá, você também pode remover o primeiro grupo de captura :)
Leo

@ Leo Esse é um bom conselho, obrigado!
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)

Funciona com qualquer nome de arquivo válido. Não há muito o que explicar aqui, o código em si é muito detalhado.

Graças ao FlipTack por me lembrar, close()não é obrigatório.


1
Você não pode simplesmente usar em f=open(...)vez da withdeclaração?
precisa saber é o seguinte

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: a nova linha à direita é significativa. Requer que o script seja chamado usando seu nome completo, incluindo a extensão. A saída começa em 0.

Como o Lote não pode editar realisticamente um arquivo, apenas adiciono linhas extras ao final do arquivo, para que ele saiba qual será o próximo número a ser impresso. O >>%0canal salva um byte porque não posso precedê-lo com um 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));}

É bem direto. Primeiro, ele lê e salva os dois caracteres na posição 0x1A no cabeçalho. Eu provavelmente poderia ter procurado mais fundo para encontrar um local mais seguro para salvar os dados, mas funciona para mim na minha máquina executando o OSX, compilado com o GCC 4.2ish e duvido que seja muito portátil. Além disso, como é baseado em caracteres, ele transborda após a 13ª iteração.

Dá a saída:

1
1
2
3
5
8
13
21
34
55

1

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

Separado com novas linhas para maior clareza, não faz parte da contagem 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()

Explicação:

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 o nome do arquivo economizaria 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)

Salvo alguns bytes graças a @Artyer


1
Como sobre isto (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

utilitários bash + Unix, 43 bytes (45-2)

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

Na primeira vez em que é executado, ele usa dc para calcular o 1º número de Fibonacci através da fórmula Binet. Cada chamada para sed modifica o programa alterando a sequência passada para dc; essa alteração informa ao dc para adicionar um 1 adicional ao expoente na fórmula, o que faz com que calcule o próximo número na sequência de Fibonacci a cada vez.

Teste

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

Para ilustrar como funciona, neste ponto, após a impressão do 55, o programa foi modificado para ler:

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

então executá-lo novamente produz

> ./fib
89

e o programa agora lê:

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

Eu gosto disso ! Bem feito !
zeppelin

@ zeppelin Obrigado - isso evita os problemas da versão anterior que tivemos.
Mitchell Spector

1

SmileBASIC 3, 99 bytes (101 -2)

Bônus de -2 bytes, porque funciona com qualquer nome de arquivo.

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 funciona, e de alguma forma acabou sendo do mesmo tamanho que o meu quebrado!


É muito mais curto se você não fizer o bônus
12Me21 11/11/17

forçar um nome de arquivo específico me faz sentir uma aberração. Eu estou batendo metade dessas respostas de qualquer maneira
snail_

Eu acho que não desligar a caixa de diálogo LOAD é muito pior.
12Me21

Na verdade, é mais curto se você o carrega no slot 1 e usa PRGEDITcomandos para substituir a primeira linha (e adicionar uma quebra de linha depois A=0B=1). E você também não precisa A=0da primeira 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")

(Tem uma nova linha à direita). Funciona com qualquer nome de arquivo 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

Empilhados, não concorrentes, 65 (67 - 2) bytes

Alguns problemas relacionados às E / S de arquivos foram corrigidos na série de confirmações mais recente. Assim, não competitivo.

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

Aqui está um link para o github.

Execução de exemplo

(Omiti o caminho real para maior clareza.)

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

Explicação

Como isso funciona é usar um par de números para iniciar a sequência ( 2:>nesse caso, é o intervalo inteiro [0, 2), ou seja (0 1)) e, em seguida, executar a transformação de Fibonacci neles, assim:

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

Em cada execução, essa transformação é executada na parte superior da pilha. Em seguida, a pilha é empurrada para a pilha, duplicada e seu primeiro membro é obtido ( stack:0#). Este item é emitido e é o número de Fibonacci desejado. reprdepois pega a representação da pilha e acrescenta uma nova linha. Em seguida, o programa é enviado para a pilha e dividido em novas linhas. Em seguida, pegamos o último membro (a última linha) e o anexamos à string mencionada acima. Finalmente, pressionamos d0(o próprio arquivo; pense em dsinal deoll 0== $0.) E escrevemos nele.


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, alternando para analisar os números como um comprimento, em vez de um número inteiro, e removendo alguns espaços perdidos.

-9 bytes removendo o espaço entre o segundo número e (let...)(o espaço mais caro de todos os tempos!).

Veja os comentários do código pré-escrito para obter uma descrição.

Testado novamente e não gera mais erros de colchete incomparáveis. Ele funciona até 7540113804746346429; nesse momento, lança uma exceção de estouro inteiro.

Observe também que isso pressupõe que o código fonte esteja localizado em "./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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.