Постійно самозмінюється код


14

Тепер ми всі знаємо, що у більшості мов є дуже прості способи "самовиправити" код. Однак що робити, якщо ви насправді змінили код і відредагували його частини ... на диску?

Ваша мета - зробити код, який друкує число, а потім редагує власний файл, щоб замінити номер наступним у послідовності Фібоначчі так:

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

Правила

  1. Ви не можете зберігати номер (и) "поза" коду. Ніяких коментарів, не повідомляючи сценарій вийти, немає EOF тощо.
  2. Якщо ваш код працює з будь-яким ім'ям файлу, відніміть 2 від суми байтів і напишіть $BYTESNOW ($ORIGINALBYTES - 2)у своєму заголовку. (Імена файлів вважаються такими, що знаходяться в межах будь-якого алфавітно-цифрового шляху до файлу.)
  3. Ваш код повинен записати висновок у файл самостійно, без будь-якої сторонньої допомоги.
  4. Ваш код може починатися з одного або нуля. Це не має значення.

8
Наступного разу, будь ласка, опублікуйте свою ідею в пісочниці замість цього і залиште публікацію там на кілька днів, щоб отримувати відгуки.
JungHwan Min

2
Чи дозволяється викликати програму шляхом виклику інтерпретатора мови програмування (наприклад perl6 program), чи потрібно включати рядок shebang, щоб її можна було викликати як ./program?
smls

1
Крім того, якщо ми не хочемо отримувати бонус -2 байти, чи можемо ми вибрати однобайтне ім’я файлу або воно повинно бути program, і чи можна вважати, що воно знаходиться в поточному робочому каталозі?
smls

Чи можна дозволити провал, коли велика кількість починає неявно перетворюватися на експоненціальне позначення?
Патрік Робертс

Чому лише 2 байти бонус? Більшість мов, напр. Луа, чи легше це зробити, "a"а не зробити arg[0]. Здається, це не варто.
Атако

Відповіді:


7

Bash, 52 47 (49-2) байт

ЗМІНИ:

  • Збережено 5 байтів, починаючи з 1 замість 0. Дякую @Leo!

Гольф

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

Тест

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

2
Я думаю, що ви могли б зберегти 1 байт, починаючи з [1 + 0] замість [-1 + 1] (див. 4 правило виклику)
Лев

2
Насправді це дозволить вам заощадити ще більше байтів, видаливши -?з регулярного вираження. А оскільки ви там, ви також можете зняти першу групу захоплення :)
Лев

@Leo Це приємна порада, дякую!
зеппелін

2

Пітон 2, 118 111 байт (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)

Він працює з будь-яким дійсним іменем файлу. Тут пояснювати не багато, сам код дуже багатослівний.

Завдяки FlipTack за те, що нагадував, close()не є обов'язковим.


1
Ви не можете просто використовувати f=open(...)замість withзаяви?
FlipTack

2

Пакетна, 81 байт

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

Примітка: останній рядок є важливим. Потрібно викликати сценарій, використовуючи його повне ім'я, включаючи розширення. Вихід починається з 0.

Оскільки Batch не може реально редагувати файл, я просто додаю зайві рядки до кінця файлу, тож з часом він дізнається, яке наступне число для друку. Місце >>%0розташування зберігає байт, тому що я не можу йому передувати цифрою.


1

C, 142 байти (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));}

Це досить прямо вперед. Спочатку він читає, потім зберігає дві символи у позиції 0x1A у заголовку. Напевно, я міг би заглянути глибше, щоб знайти більш безпечне місце для збереження даних, але це працює для мене на моїй машині під керуванням OSX, складеній з GCC 4.2ish, і я сумніваюся, що це дуже портативно. Крім того, оскільки він заснований на знаках, він переповнюється після 13-ї ітерації.

Це дає вихід:

1
1
2
3
5
8
13
21
34
55

1

Node.js, 152 137 байт (139 - 2)

Відокремлено новими рядками для наочності, не входить до числа байтів.

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

Пояснення:

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

Використання:

// 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) байт

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)

жорстке кодування імені файлу дозволило б зберегти 5 байт (88 байт):

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
Як щодо цього (88 байт)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 утиліти, 43 байти (45-2)

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

При першому запуску він використовує dc для обчислення 1-го числа Фібоначчі за формулою Binet. Кожен виклик sed змінює програму, змінюючи рядок, переданий на dc; ця зміна спонукає dc додавати додатковий 1 до показника у формулі, що змушує кожного разу обчислювати наступне число у послідовності Фібоначчі.

Тест

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

Щоб проілюструвати, як це працює, в цей момент після друку 55-го програма була змінена на наступне:

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

так що запуск його знову дає результат

> ./fib
89

і програма тепер говорить:

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

Мені подобається це ! Молодці!
zeppelin

@zeppelin Дякую - це дозволяє уникнути проблем із попередньою версією, яку ми мали.
Мітчелл Спектор

1

SmileBASIC 3, 99 байт (101 -2)

-2 байт бонус, оскільки він працює з будь-яким ім'ям файлу.

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

Цей працює, і він якось закінчився тим самим розміром, що і мій зламаний!


Це набагато коротше, якщо ви не зробите бонус
12Me21

примушування конкретного імені файлів викликає відчуття виродка. Я все одно
перебиваю

Я думаю, що не вимкнути діалог ЗАВАНТАЖЕННЯ набагато гірше.
12Me21

Насправді це коротше, якщо ви завантажуєте його в слот 1 і використовуєте PRGEDITкоманди для заміни першого рядка (а після цього додаєте рядок рядка A=0B=1) І вам також не потрібен A=0перший час.
12Me21

0

R, 145 байт (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")

(Має останній рядок). Він працює з будь-яким дійсним іменем файлу.


0

Perl 6 , 67 62 байт (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

65 (67 - 2) байтів, що складаються без конкуренції

Деякі проблеми щодо IO файлів були виправлені в останніх серіях комітетів. Таким чином, неконкурентоспроможний.

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

Ось посилання на github.

Приклад виконання

(Я пропустив фактичний шлях для ясності.)

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

Пояснення

Як це працює, взявши пару чисел, щоб почати послідовність ( 2:>у даному випадку це цілий діапазон [0, 2), який є (0 1)), а потім виконати перетворення Фібоначчі на них так:

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

При кожному прогоні це перетворення виконується у верхній частині стека. Потім стек висувається на стек, дублюється, і його перший член виходить ( stack:0#). Потім цей пункт виводиться і є бажаним числом Фібоначчі. reprпотім приймає представлення стека і додає новий рядок. Потім програма висувається на стек і розбивається на нові рядки. Потім беремо останній член (останній рядок) і додаємо це до вищезгаданого рядка. Нарешті, ми натискаємо d0(сам файл; думаю dollar знак 0== $0.) І записуємо в нього.



0

Clojure, 209 204 195 байт

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 байт, перемикаючи на розбір чисел як довге замість цілого числа та видаляючи пару пропущених пробілів.

-9 байт, видаливши пробіл між другим числом і (let...) (найдорожчим пробілом коли-небудь!).

Ознайомтесь із коментарями коду, який попередньо готовий до опису.

Перевірений знову, і він більше не викидає неперевершені помилки дужок. Він працює до 7540113804746346429, після чого він видає цілий виняток переповнення.

Також зауважте, це передбачає, що вихідний код знаходиться за адресою "./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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.