Постоянно самоизменяющийся код

14

Теперь мы все знаем, что большинство языков имеют очень простые способы «самоизменения» кода. Тем не менее, что если бы вы на самом деле изменили код и отредактировали его части ... на диске?

Ваша цель - создать код, который печатает число, а затем редактирует свой собственный файл, чтобы заменить число следующим в последовательности Фибоначчи, например:

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

правила

  1. Вы не можете хранить число «вне» кода. Без комментариев, без указания сценария на выход, без EOF и т. Д.
  2. Если ваш код работает с любым именем файла, вычтите 2 из вашего байта и напишите $BYTESNOW ($ORIGINALBYTES - 2)в своем заголовке. (Предполагается, что имена файлов находятся в пределах любого алфавитно-цифрового пути к файлу.)
  3. Ваш код должен записать вывод в файл сам по себе, без какой-либо внешней помощи по трубопроводам.
  4. Ваш код может начинаться с единицы или с нуля. Это не важно
NO_BOOT_DEVICE
источник
8
В следующий раз, пожалуйста, опубликуйте свою идею в Песочнице и оставьте пост там на несколько дней, чтобы получить отзыв.
JungHwan Мин
2
Разрешено ли вызывать программу, вызывая интерпретатор языка программирования (например perl6 program), или она должна включать строку shebang, чтобы ее можно было назвать как ./program?
смс
1
Кроме того, если мы не хотим использовать бонус -2 байта, можем ли мы выбрать однобайтовое имя файла или оно должно быть program, и можем ли мы предположить, что оно находится в текущем рабочем каталоге?
смс
Можно ли допустить сбой, когда большие числа начинают неявно преобразовываться в экспоненциальную запись?
Патрик Робертс
Почему только 2 байта бонуса? Большинство языков, например Луа, проще сделать "a"вместо arg[0]. Это не стоит того.
ATaco

Ответы:

7

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

правок:

  • Сохранено 5 байтов, начиная с 1 вместо 0. Спасибо @Leo!

Golfed

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
@ Leo Это хороший совет, спасибо!
Цеппелин
2

Python 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.

Так как пакетный режим не может реально отредактировать файл, я просто добавляю дополнительные строки в конец файла, чтобы в итоге он узнал, какой будет следующий номер для печати. >>%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
Ahemone
источник
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-го числа Фибоначчи по формуле Бине. Каждый вызов 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 Спасибо - это позволяет избежать проблем с предыдущей версией, которая у нас была.
Митчелл Спектор
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

Этот действительно работает, и он каким-то образом оказался того же размера, что и мой сломанный!

snail_
источник
Это намного короче, если вы не делаете бонус
12Me21
заставляя определенное имя файла заставлять меня чувствовать себя уродом. В любом случае я
бью
Я думаю, что отключение диалога LOAD намного хуже.
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")

(Имеет завершающий перевод строки). Он работает с любым допустимым именем файла.

plannapus
источник
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}/}}
SMLS
источник
0

Сложенный, неконкурентный, 65 (67 - 2) байтов

Некоторые проблемы, касающиеся файлового ввода-вывода, были исправлены в самой последней серии коммитов. Таким образом, неконкурентоспособен.

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(сам файл; думаю dзнак ollar 0== $0.) И пишем в него.

Конор О'Брайен
источник
0

Рубин, 68 байт (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 байтов

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...) (самое дорогое место за всю историю!).

См. Комментарии к pregolfed-коду для описания.

Протестировано снова, и больше не выдает непревзойденных ошибок в скобках. Он работает до 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
Carcigenicate
источник