Kalıcı olarak kendini değiştiren kod


14

Artık hepimiz biliyoruz ki çoğu dilde "kendini değiştirmek" için çok basit yollar var. Ancak, kodu gerçekten değiştirip diskin bazı bölümlerini ...

Amacınız bir sayı yazdıran kod oluşturmak ve daha sonra sayıyı Fibonacci dizisindeki bir sonraki ile değiştirmek için kendi dosyasını düzenler:

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

kurallar

  1. Kodun "dışında" sayılarını kaydedemezsiniz. Yorum yok, betiğin çıkmasını söyleme yok, EOF yok, vb.
  2. Kodunuz herhangi bir dosya $BYTESNOW ($ORIGINALBYTES - 2)adıyla çalışıyorsa , bayt miktarınızdan 2 çıkartın ve başlığınıza yazın. (Dosya adlarının herhangi bir alfasayısal dosya yolu aralığında olduğu varsayılır.)
  3. Kodunuzun, dış boru hattı yardımı olmadan çıktıyı tek başına dosyaya yazması gerekir.
  4. Kodunuz bir veya sıfırdan başlayabilir. Önemli değil.

8
Bir dahaki sefere, fikrinizi Sandbox'a gönderin ve geri bildirim almak için yayını birkaç gün orada bırakın.
JungHwan Min

2
Programlama dilinin tercümanını (ör. perl6 program) Çevirerek programı çağırmaya izin veriliyor mu veya olarak adlandırılabilmesi için shebang satırını içermesi gerekiyor ./programmu?
17'de smls

1
Ayrıca, -2 bayt bonusu için gitmek istemiyorsak, tek baytlık bir dosya adı seçebilir miyiz veya olması gerekir programmi ve geçerli çalışma dizininde olduğunu varsayabilir miyiz?
smls

Büyük sayılar örtülü olarak üstel gösterime dönüşmeye başladığında başarısız olmasına izin verilebilir mi?
Patrick Roberts

Neden sadece 2 bayt bonus? Çoğu dil, Ör. Lua, "a"bunun yerine daha kolay yap arg[0]. Buna değmez.
ATaco

Yanıtlar:


7

Bash, 52 47 (49-2) bayt

DÜZENLEMELER:

  • 0 yerine 1 ile başlayarak 5 bayt kaydedildi. Thanks @Leo!

golfed

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

Ölçek

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

2
Sanırım [-1 + 1] yerine [1 + 0] 'dan başlayarak 1 bayt kurtarabileceğinizi düşünüyorum (meydan okumanın 4. kuralına bakın)
Leo

2
Aslında, -?normal ifadeden kaldırarak daha fazla bayt tasarruf etmenizi sağlar . Ve orada olduğunuzdan, ilk yakalama grubunu da kaldırabilirsiniz :)
Leo

@Leo Bu güzel bir tavsiye, teşekkürler!
zeppelin

2

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

Geçerli herhangi bir dosya adıyla çalışır. Burada açıklanacak çok şey yok, kodun kendisi çok ayrıntılı.

Bana hatırlatmak için FlipTack sayesinde close()zorunlu değil.


1
Açıklama f=open(...)yerine sadece kullanamaz withmısın?
FlipTack

2

Toplu, 81 bayt

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

Not: sondaki yeni satır önemlidir. Komut dosyasının, uzantı da dahil olmak üzere tam adını kullanarak çağrılmasını gerektirir. Çıkış 0'dan başlar.

Batch bir dosyayı gerçekçi bir şekilde düzenleyemediğinden, dosyanın sonuna fazladan satır ekliyorum, bu nedenle sonunda bir sonraki yazdırılacak sayının hangisi olduğunu bilecek. >>%0Yerleşim Ben bir rakam ile kendisinden önce olamaz çünkü bir bayt kaydeder.


1

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

Oldukça düz ileri. Önce okuduktan sonra iki karakteri başlıktaki 0x1A konumuna kaydeder. Muhtemelen verileri kaydetmek için daha güvenli bir yer bulmak için daha derinlere bakabilirdim ama benim için OSX çalıştıran, GCC 4.2ish ile derlenmiş makinemde çalışıyor ve çok taşınabilir olduğundan şüpheliyim. Ayrıca, karakterlere dayandığı için 13. tekrardan sonra taşar.

Çıktı verir:

1
1
2
3
5
8
13
21
34
55

1

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

Bayt sayımının bir parçası değil, netlik için yeni satırlarla ayrılmıştır.

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

Açıklama:

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

Kullanımı:

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

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)

dosya adının sabit kodlanması 5 bayt (88 bayt) tasarruf sağlar:

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 sayesinde bazı baytlar kaydetti


1
Buna ne dersin (88 bayt)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 yardımcı programları, 43 bayt (45-2)

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

Bu ilk çalıştırıldığında, Binet formülü aracılığıyla 1. Fibonacci numarasını hesaplamak için dc kullanır. Sed'e yapılan her çağrı, dc'ye aktarılan dizeyi değiştirerek programı değiştirir; bu değişiklik dc'ye formüldeki üsse ek bir 1 eklemesini söyler, bu da her seferinde Fibonacci dizisindeki bir sonraki sayıyı hesaplamasına neden olur.

Ölçek

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

Nasıl çalıştığını göstermek için, bu noktada, 55 yazdırıldıktan sonra, program okumak için değiştirildi:

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

böylece tekrar çalıştırmak

> ./fib
89

ve program şimdi okuyor:

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

Bunu severim ! Aferin !
zeplin

@zeppelin Teşekkür ederim - bu, önceki sürümümüzle ilgili sorunları önler.
Mitchell Spector

1

SmileBASIC 3, 99 bayt (101 -2)

-2 bayt bonusu çünkü herhangi bir dosya adıyla çalışır.

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

Bu işe yarıyor ve her nasılsa benim kırık olanımla aynı boyutta oldu!


Bonusu yapmazsanız çok daha kısa
12Me21

belirli bir dosya adını zorlamak beni bir ucube gibi hissettiriyor. Bu cevapların yarısını yine de
atıyorum

YÜK iletişim kutusunu kapatmamak çok daha kötü olduğunu düşünüyorum.
12Me21

Yuva 1'e PRGEDITyüklerseniz ve ilk satırı değiştirmek için komutlar kullanırsanız (ve sonra bir satır sonu eklerseniz A=0B=1) aslında daha kısadır ve ayrıca A=0ilk kez ihtiyacınız yoktur .
12Me21

0

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

(Sonunda bir satırsonu vardır). Geçerli herhangi bir dosya adıyla çalışır.


0

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

Yığılmış, rakipsiz, 65 (67-2) bayt

Dosya G / Ç ile ilgili bazı sorunlar en son taahhüt serilerinde giderildi. Böylece, rakipsiz.

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

İşte github'a bir bağlantı.

Örnek yürütme

(Açıklık için gerçek yolu atladım.)

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

açıklama

Bu nasıl çalışır (diziyi başlatmak için sayıların bir çift alarak gereğidir 2:>bu durumda tamsayı aralığıdır [0, 2)olduğunu (0 1)o zaman gibi üzerlerinde Fibonacci dönüşümü gerçekleştirerek,):

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

Her çalıştırmada, bu dönüşüm yığının üstünde yürütülür. Daha sonra, yığın yığına itilir, çoğaltılır ve ilk üyesi elde edilir ( stack:0#). Bu madde daha sonra çıkarılır ve istenen Fibonacci numarasıdır. reprsonra yığının temsilini alır ve bir satırsonu ekler. Ardından, program yığına itilir ve yeni satırlara bölünür. Ardından, son üyeyi (son satır) alırız ve bunu yukarıda belirtilen dizeye ekleriz. Son olarak, itiyoruz d0(dosyanın kendisi; dollar sign 0== $0.) Ve yazıyoruz.



0

Clojure, 209 204 195 bayt

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

Sayıları bir tamsayı yerine uzun olarak ayrıştırmak ve birkaç cevapsız boşluğu kaldırmak için -5 bayt.

İkinci sayı ile (let...)(şimdiye kadarki en pahalı alan!) Arasındaki boşluğu kaldırarak -9 bayt .

Açıklama için önceden golf koduna bakınız.

Tekrar test edildi ve artık eşleşmeyen parantez hataları atmıyor. 7540113804746346429'a kadar çalışır, bu noktada bir tamsayı taşması istisnası atar.

Ayrıca, kaynak kodun "./src/s.clj" konumunda olduğunu varsayarsınız.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.