Mã tự sửa đổi vĩnh viễn


14

Bây giờ, tất cả chúng ta đều biết hầu hết các ngôn ngữ có những cách rất đơn giản để "tự sửa đổi" mã. Tuy nhiên, điều gì sẽ xảy ra nếu bạn thực sự sửa đổi mã và chỉnh sửa các phần của nó ... trên đĩa?

Mục tiêu của bạn là tạo mã in một số, sau đó chỉnh sửa tệp của chính nó để thay thế số đó bằng mã tiếp theo trong chuỗi Fibonacci như vậy:

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

Quy tắc

  1. Bạn không được lưu trữ số (bên ngoài) của mã. Không có bình luận, không nói cho kịch bản thoát ra, không có EOF, v.v.
  2. Nếu mã của bạn hoạt động với bất kỳ tên tệp nào, hãy trừ 2 từ số lượng byte của bạn và viết $BYTESNOW ($ORIGINALBYTES - 2)tiêu đề của bạn. (Tên tệp được giả sử là nằm trong phạm vi của bất kỳ đường dẫn tệp chữ và số nào.)
  3. Mã của bạn phải tự ghi đầu ra vào tệp mà không cần bất kỳ sự trợ giúp đường ống bên ngoài nào.
  4. Mã của bạn có thể bắt đầu từ một hoặc không. Nó không thành vấn đề.

8
Lần tới, xin vui lòng gửi ý tưởng của bạn trong Sandbox thay vào đó và để lại bài đăng ở đó trong vài ngày để nhận phản hồi.
JungHwan Min

2
Được phép gọi chương trình bằng cách gọi trình thông dịch của ngôn ngữ lập trình (ví dụ perl6 program), hay nó phải bao gồm dòng shebang để nó có thể được gọi là ./program?
smls

1
Ngoài ra, nếu chúng ta không muốn nhận phần thưởng -2 byte, chúng ta có thể chọn tên tệp một byte hay không program, và chúng ta có thể giả sử nó nằm trong thư mục làm việc hiện tại không?
smls

Nó có thể được phép thất bại khi số lượng lớn bắt đầu chuyển đổi hoàn toàn sang ký hiệu số mũ không?
Patrick Roberts

Tại sao chỉ có 2 byte tiền thưởng? Hầu hết các ngôn ngữ, ví dụ. Lua, có dễ dàng hơn chỉ để làm "a"thay vì arg[0]. Nó không có vẻ đáng giá.
ATaco

Câu trả lời:


7

Bash, 52 47 (49-2) byte

CHỈNH SỬA:

  • Đã lưu 5 byte, bằng cách bắt đầu bằng 1 thay vì 0. Cảm ơn @Leo!

Chơi gôn

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

Kiểm tra

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

2
Tôi nghĩ bạn có thể tiết kiệm 1 byte bằng cách bắt đầu từ [1 + 0] thay vì [-1 + 1] (xem quy tắc thứ 4 của thử thách)
Leo

2
Trên thực tế, điều đó sẽ khiến bạn tiết kiệm được nhiều byte hơn bằng cách xóa -?khỏi regex. Và vì bạn đang ở đó, bạn cũng có thể xóa nhóm bắt giữ đầu tiên :)
Leo

@Leo Đó là một lời khuyên tốt đẹp, cảm ơn bạn!
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)

Nó hoạt động với bất kỳ tên tệp hợp lệ. Không có nhiều điều để giải thích ở đây, bản thân mã rất dài dòng.

Cảm ơn FlipTack đã nhắc nhở tôi, close()không bắt buộc.


1
Bạn không thể sử dụng f=open(...)thay vì withtuyên bố?
FlipTack

2

Mẻ, 81 byte

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

Lưu ý: dòng mới theo dõi là đáng kể. Yêu cầu tập lệnh được gọi bằng tên đầy đủ của nó bao gồm cả phần mở rộng. Đầu ra bắt đầu từ 0.

Vì Batch không thể chỉnh sửa một cách thực tế một tệp, tôi chỉ cần thêm các dòng bổ sung vào cuối tệp, vì vậy cuối cùng nó sẽ biết số tiếp theo sẽ được in là gì. Các >>%0vị trí tiết kiệm một byte bởi vì tôi không thể trước nó với một chữ số.


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

Nó khá thẳng về phía trước. Đầu tiên nó đọc sau đó lưu hai ký tự ở vị trí 0x1A trong tiêu đề. Tôi có lẽ đã có thể nhìn sâu hơn để tìm một vị trí an toàn hơn để lưu dữ liệu nhưng nó hoạt động với tôi trên máy chạy OSX, được biên dịch với GCC 4.2ish và tôi nghi ngờ nó rất dễ mang theo. Ngoài ra, vì nó dựa trên ký tự, nó tràn ra sau lần lặp thứ 13.

Nó cho đầu ra:

1
1
2
3
5
8
13
21
34
55

1

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

Được phân tách bằng các dòng mới cho rõ ràng, không phải là một phần của số byte.

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

Giải trình:

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

Sử dụng:

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

mã hóa tên tệp sẽ tiết kiệm 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)

Đã lưu một số byte nhờ @Artyer


1
Làm thế nào về điều này (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)#
Artyer

1

bash + tiện ích Unix, 43 byte (45-2)

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

Lần đầu tiên được chạy, nó sử dụng dc để tính toán số Fibonacci thứ 1 thông qua công thức Binet. Mỗi lệnh gọi sed sửa đổi chương trình bằng cách thay đổi chuỗi được truyền thành dc; thay đổi này cho biết dc thêm 1 vào số mũ trong công thức, điều này khiến nó tính toán số tiếp theo trong chuỗi Fibonacci mỗi lần.

Kiểm tra

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

Để minh họa cách thức hoạt động, tại thời điểm này, sau khi 55 được in, chương trình đã được sửa đổi để đọc:

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

vì vậy chạy nó một lần nữa mang lại

> ./fib
89

và chương trình hiện đọc:

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

Tôi thích điều này ! Làm tốt !
zeppelin

@zeppelin Cảm ơn bạn - điều này tránh được các vấn đề với phiên bản trước mà chúng tôi có.
Spector Mitchell

1

SmileBASIC 3, 99 byte (101 -2)

Phần thưởng -2 byte vì nó hoạt động với bất kỳ tên tệp nào.

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

Cái này không hoạt động, và bằng cách nào đó nó đã có cùng kích thước với cái bị hỏng của tôi!


Sẽ ngắn hơn nhiều nếu bạn không thực hiện phần thưởng
12Me21

buộc một tên tệp cụ thể làm cho tôi cảm thấy như một kẻ lập dị. Dù sao tôi cũng đang đánh bại một nửa những câu trả lời này
snail_

Tôi nghĩ rằng không tắt hộp thoại LOAD là tồi tệ hơn nhiều.
12Me21

Nó thực sự ngắn hơn nếu bạn tải nó vào khe 1 và sử dụng PRGEDITcác lệnh để thay thế dòng đầu tiên (và thêm ngắt dòng sau A=0B=1) Và bạn cũng không cần A=0lần đầu tiên.
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")

(Có một dòng mới). Nó hoạt động với bất kỳ tên tệp hợp lệ.


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}/}}

0

Các byte được xếp chồng, không biên dịch, 65 (67 - 2)

Một số vấn đề liên quan đến tệp IO đã được sửa trong loạt cam kết gần đây nhất. Như vậy, không thành phần.

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

Đây là một liên kết đến github.

Ví dụ thực hiện

(Tôi đã bỏ qua đường dẫn thực tế cho rõ ràng.)

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

Giải trình

Cách làm việc này là bằng cách lấy một cặp số để bắt đầu chuỗi ( 2:>trong trường hợp này là phạm vi số nguyên [0, 2), đó là (0 1)), sau đó thực hiện việc chuyển đổi Fibonacci trên chúng, như vậy:

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

Trên mỗi lần chạy, phép chuyển đổi này được thực hiện trên đỉnh của ngăn xếp. Sau đó, ngăn xếp được đẩy lên ngăn xếp, nhân đôi và thành viên đầu tiên của nó thu được ( stack:0#). Mục này sau đó được xuất ra và là số Fibonacci mong muốn. reprsau đó lấy đại diện của ngăn xếp và nối thêm một dòng mới. Sau đó, chương trình được đẩy lên ngăn xếp và phân chia trên dòng mới. Sau đó, chúng tôi lấy thành viên cuối cùng (dòng cuối cùng) và nối nó vào chuỗi đã nói ở trên. Cuối cùng, chúng tôi đẩy d0(chính tệp; nghĩ dký hiệu ollar 0== $0.) Và viết cho nó.



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 bằng cách chuyển sang phân tích các số dưới dạng dài thay vì số nguyên và xóa một vài khoảng trắng bị bỏ lỡ.

-9 byte bằng cách xóa khoảng trắng giữa số thứ hai và (let...) (không gian đắt nhất từng có!).

Xem các bình luận mã pregolfed cho một mô tả.

Đã kiểm tra lại, và nó không còn ném các lỗi khung chưa từng có. Nó hoạt động lên đến 7540113804746346429, tại thời điểm đó, nó ném một ngoại lệ tràn số nguyên.

Cũng lưu ý, điều này giả sử mã nguồn được đặt tại "./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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.