Kode modifikasi otomatis secara permanen

14

Sekarang, kita semua tahu sebagian besar bahasa memiliki cara yang sangat sederhana untuk "memodifikasi sendiri" kode. Namun, bagaimana jika Anda benar-benar memodifikasi kode dan mengeditnya ... pada disk?

Tujuan Anda adalah membuat kode yang mencetak angka, lalu mengedit file sendiri untuk mengganti nomor dengan yang berikutnya dalam urutan Fibonacci seperti:

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

Aturan

  1. Anda tidak boleh menyimpan nomor "di luar" dari kode. Tidak ada komentar, tidak ada yang memberitahu skrip untuk keluar, tidak ada EOF, dll.
  2. Jika kode Anda berfungsi dengan nama file apa pun, kurangi 2 dari jumlah byte Anda dan tulis $BYTESNOW ($ORIGINALBYTES - 2)di judul Anda. (Nama file diasumsikan berada dalam kisaran jalur file alfanumerik apa pun.)
  3. Kode Anda harus menulis output ke file itu sendiri, tanpa bantuan perpipaan eksternal.
  4. Kode Anda dapat mulai dari satu atau nol. Itu tidak masalah.
NO_BOOT_DEVICE
sumber
8
Lain kali, silakan kirim ide Anda di Sandbox dan tinggalkan pos di sana selama beberapa hari untuk menerima umpan balik.
JungHwan Min
2
Apakah diizinkan memanggil program dengan memanggil penerjemah bahasa pemrograman (mis. perl6 program), Atau harus menyertakan baris shebang sehingga dapat disebut sebagai ./program?
smls
1
Juga, jika kita tidak ingin menggunakan bonus -2 byte, dapatkah kita memilih nama file byte tunggal atau haruskah demikian program, dan dapatkah kita menganggapnya terletak di direktori kerja saat ini?
smls
Bisakah itu dibiarkan gagal ketika sejumlah besar mulai secara implisit mengkonversi ke notasi eksponensial?
Patrick Roberts
Mengapa hanya 2 byte bonus? Sebagian besar bahasa, mis. Lua, lebih mudah lakukan saja "a"alih - alih arg[0]. Tampaknya tidak sepadan.
ATaco

Jawaban:

7

Pesta, 52 47 (49-2) byte

EDIT:

  • Disimpan 5 byte, dengan memulai dengan 1 bukannya 0. Terima kasih @ Leo!

Golf

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

Uji

>for i in `seq 10`
> do
> ./fibo
> done
1
1
2
3
5
8
13
21
34
55
zeppelin
sumber
2
Saya pikir Anda bisa menghemat 1 byte dengan mulai dari [1 + 0] alih-alih [-1 + 1] (lihat aturan ke-4 tantangan)
Leo
2
Sebenarnya, itu akan membuat Anda menghemat lebih banyak byte dengan menghapus -?dari regex. Dan karena Anda berada di sana, Anda juga dapat menghapus grup penangkap pertama :)
Leo
@ Leo Itu saran yang bagus, terima kasih!
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)

Ini berfungsi dengan nama file yang valid. Tidak banyak yang bisa dijelaskan di sini, kodenya sendiri sangat verbose.

Berkat FlipTack untuk mengingatkan saya, close()tidak wajib.

Gurupad Mamadapur
sumber
1
Tidak bisakah Anda menggunakan f=open(...)bukan withpernyataan itu?
FlipTack
2

Batch, 81 byte

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

Catatan: trailing newline sangat penting. Memerlukan skrip untuk dipanggil menggunakan nama lengkapnya termasuk ekstensi. Output dimulai pada 0.

Karena Batch tidak dapat mengedit file secara realistis, saya hanya menambahkan baris tambahan di akhir file, jadi pada akhirnya ia akan tahu nomor mana yang akan dicetak. The >>%0penempatan menyimpan byte karena saya tidak bisa mendahului dengan digit.

Neil
sumber
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));}

Cukup lurus ke depan. Pertama ia membaca kemudian menyimpan dua karakter di posisi 0x1A di header. Saya mungkin bisa melihat lebih dalam untuk menemukan tempat yang lebih aman untuk menyimpan data tetapi itu bekerja untuk saya di mesin saya yang menjalankan OSX, dikompilasi dengan GCC 4.2ish dan saya ragu itu sangat portabel. Juga, karena didasarkan pada karakter itu meluap setelah iterasi ke-13.

Ini memberikan output:

1
1
2
3
5
8
13
21
34
55
Ahemone
sumber
1

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

Dipisahkan dengan baris baru untuk kejelasan, bukan bagian dari jumlah byte.

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

Penjelasan:

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

Pemakaian:

// 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
sumber
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 nama file akan menghemat 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)

Menyimpan beberapa byte berkat @Artyer

ovs
sumber
1
Bagaimana dengan ini (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 + Utilitas Unix, 43 byte (45-2)

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

Pertama kali dijalankan, ini menggunakan dc untuk menghitung angka Fibonacci 1 melalui rumus Binet. Setiap panggilan ke sed memodifikasi program dengan mengubah string yang dilewatkan ke dc; perubahan ini memberitahu dc untuk menambahkan 1 tambahan ke eksponen dalam rumus, yang menyebabkannya menghitung angka berikutnya dalam urutan Fibonacci setiap kali.

Uji

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

Untuk menggambarkan cara kerjanya, pada titik ini, setelah 55 dicetak, program telah dimodifikasi untuk membaca:

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

jadi menjalankannya lagi menghasilkan

> ./fib
89

dan program sekarang berbunyi:

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
sumber
Saya suka ini ! Sudah selesai dilakukan dengan baik !
zeppelin
@zeppelin Terima kasih - ini menghindari masalah dengan versi sebelumnya yang kami miliki.
Mitchell Spector
1

SmileBASIC 3, 99 byte (101 -2)

Bonus -2 byte karena berfungsi dengan nama file apa pun.

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

Yang ini berhasil, dan entah bagaimana ukurannya sama dengan yang saya rusak!

siput_
sumber
Ini jauh lebih pendek jika Anda tidak melakukan bonus
12Me21
memaksa nama file tertentu membuat saya merasa seperti orang aneh. Saya mengalahkan setengah jawaban ini
snail_
Saya pikir tidak mematikan dialog LOAD jauh lebih buruk.
12Me21
Ini sebenarnya lebih pendek jika Anda memuatnya ke slot 1 dan menggunakan PRGEDITperintah untuk mengganti baris pertama (dan menambahkan linebreak setelah A=0B=1) Dan Anda juga tidak perlu A=0yang pertama kali.
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")

(Memiliki baris baru tertinggal). Ini berfungsi dengan nama file yang valid.

plannapus
sumber
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}/}}
seseorang
sumber
0

Ditumpuk, tidak bersaing, 65 (67 - 2) byte

Beberapa masalah tentang file IO diperbaiki dalam serangkaian komit terbaru. Dengan demikian, tidak bersaing.

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

Berikut tautan ke github.

Contoh eksekusi

(Saya menghilangkan jalan yang sebenarnya untuk kejelasan.)

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

Penjelasan

Cara kerjanya adalah dengan mengambil sepasang angka untuk memulai urutan ( 2:>dalam hal ini adalah kisaran integer [0, 2), yaitu (0 1)), kemudian melakukan transformasi Fibonacci pada mereka, seperti:

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

Pada setiap proses, transformasi ini dijalankan di bagian atas tumpukan. Kemudian, tumpukan didorong ke tumpukan, digandakan, dan anggota pertamanya diperoleh ( stack:0#). Item ini kemudian dikeluarkan, dan merupakan angka Fibonacci yang diinginkan. reprkemudian mengambil representasi tumpukan dan menambahkan baris baru. Kemudian, program didorong ke stack, dan dibagi pada baris baru. Kemudian, kami mengambil anggota terakhir (baris terakhir), dan menambahkan ini ke string yang disebutkan di atas. Akhirnya, kami mendorong d0(file itu sendiri; pikirkan dtanda ollar 0== $0.) Dan menulis untuk itu.

Conor O'Brien
sumber
0

Ruby, 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
Nilai Tinta
sumber
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 dengan beralih untuk mem-parsing angka sebagai panjang alih-alih bilangan bulat, dan menghapus beberapa spasi yang terlewatkan.

-9 byte dengan menghapus spasi antara angka kedua dan (let...) (ruang termahal yang pernah ada!).

Lihat komentar kode pregolfed untuk deskripsi.

Diuji lagi, dan tidak lagi melempar kesalahan braket yang tidak cocok. Ia bekerja hingga 7540113804746346429, pada saat itu ia melempar pengecualian bilangan bulat bilangan bulat.

Perhatikan juga, ini mengasumsikan kode sumber terletak di "./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
sumber