14
\$\begingroup\$

Now, we all know most languages have very simple ways to "self-modify" code. However, what if you were to actually modify the code and edit parts of it...on disk?

Your goal is to make code that prints a number, then edits its own file to replace the number with the next one in the Fibonacci sequence like so:

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

Rules

  1. You may not store the number(s) "outside" of the code. No comments, no telling the script to exit, no EOF, etc.
  2. If your code works with any filename, subtract 2 from your byte amount and write $BYTESNOW ($ORIGINALBYTES - 2) in your title. (Filenames are assumed to be within the range of any alphanumeric file path.)
  3. Your code must write the output to the file on it's own, without any external piping assistance.
  4. Your code can start from one or zero. It doesn't matter.
\$\endgroup\$
  • 8
    \$\begingroup\$ Next time, please post your idea in the Sandbox instead and leave the post there for a few days to receive feedback. \$\endgroup\$ – JungHwan Min Jan 22 '17 at 1:50
  • 2
    \$\begingroup\$ Is it allowed to call the program by invoking the interpreter of the programming language (e.g. perl6 program), or does it have to include the shebang line so that it can be called as ./program? \$\endgroup\$ – smls Jan 22 '17 at 11:37
  • 1
    \$\begingroup\$ Also, if we don't want to go for the -2 bytes bonus, can we choose a single-byte filename or does it have to be program, and can we assume it's located in the current working directory? \$\endgroup\$ – smls Jan 22 '17 at 11:44
  • \$\begingroup\$ Can it be allowed to fail when large numbers begin implicitly converting to exponential notation? \$\endgroup\$ – Patrick Roberts Jan 22 '17 at 18:40
  • \$\begingroup\$ Why only 2 bytes bonus? Most languages, Eg. Lua, have it easier just to do "a" instead of arg[0]. It doesn't seem worth it. \$\endgroup\$ – ATaco Jan 22 '17 at 22:20

13 Answers 13

7
\$\begingroup\$

Bash, 52 47 (49-2) bytes

EDITS:

  • Saved 5 bytes, by starting with 1 instead of 0. Thanks @Leo !

Golfed

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

Test

>for i in `seq 10`
> do
> ./fibo
> done
1
1
2
3
5
8
13
21
34
55
\$\endgroup\$
  • 2
    \$\begingroup\$ I think you could save 1 byte by starting from [1+0] instead of [-1+1] (see 4th rule of the challenge) \$\endgroup\$ – Leo Jan 22 '17 at 11:57
  • 2
    \$\begingroup\$ Actually, that would make you save even more bytes by removing the -? from the regex. And since you're there, you could also remove the first capturing group :) \$\endgroup\$ – Leo Jan 22 '17 at 12:01
  • \$\begingroup\$ @Leo That's a nice advice, thank you ! \$\endgroup\$ – zeppelin Jan 22 '17 at 12:25
2
\$\begingroup\$

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

It works with any valid filename. There is not much to explain here, the code itself is very verbose.

Thanks to FlipTack for reminding me, close() is not mandatory.

\$\endgroup\$
  • 1
    \$\begingroup\$ Can't you just use f=open(...) instead of the with statement? \$\endgroup\$ – FlipTack Jan 22 '17 at 9:52
2
\$\begingroup\$

Batch, 81 bytes

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

Note: the trailing newline is significant. Requires the script to be invoked using its full name including extension. Output starts at 0.

Since Batch can't realistically edit a file, I just add extra lines to the end of the file, so eventually it will know which the next number to print is. The >>%0 placement saves a byte because I can't precede it with a digit.

\$\endgroup\$
1
\$\begingroup\$

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

It's pretty straight forward. First it reads then saves the two chars at position 0x1A in the header. I probably could've looked deeper to find a safer spot to save the data but it works for me on my machine running OSX, compiled with GCC 4.2ish and I doubt it's very portable. Also, since it's based off chars it overflows after the 13th iteration.

It gives the output:

1
1
2
3
5
8
13
21
34
55
\$\endgroup\$
1
\$\begingroup\$

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

Separated with newlines for clarity, not part of byte count.

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

Explanation:

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

Usage:

// assuming above script is stored in program.js
$ node program
1
$ node program
1
$ node program
2
$ node program
3
$ node program
5
...
\$\endgroup\$
1
\$\begingroup\$

Python 3.6, 96 91 (93-2) bytes

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 the filename would save 5 bytes (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)

Saved some bytes thanks to @Artyer

\$\endgroup\$
  • 1
    \$\begingroup\$ How about this (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)# \$\endgroup\$ – Artyer Jan 23 '17 at 15:42
1
\$\begingroup\$

bash + Unix utilities, 43 bytes (45-2)

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

The first time this is run, it uses dc to compute the 1st Fibonacci number via the Binet formula. Each call to sed modifies the program by changing the string passed to dc; this change tells dc to add an additional 1 to the exponent in the formula, which causes it to compute the next number in the Fibonacci sequence each time.

Test

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

To illustrate how it works, at this point, after the 55 is printed, the program has been modified to read:

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

so running it again yields

> ./fib
89

and the program now reads:

dc -e9k5v1+2/z1+1+1+1+1+1+1+1+1+1+1+^5v/.5+0k1/p;sed -i s/z/z1+/ $0
\$\endgroup\$
  • \$\begingroup\$ I like this ! Well done ! \$\endgroup\$ – zeppelin Jan 24 '17 at 19:49
  • \$\begingroup\$ @zeppelin Thank you -- this avoids the issues with the previous version we had. \$\endgroup\$ – Mitchell Spector Jan 24 '17 at 19:50
1
\$\begingroup\$

SmileBASIC 3, 99 bytes (101 -2)

-2 byte bonus because it works with any filename.

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

This one does work, and it somehow ended up being the same size as my broken one!

\$\endgroup\$
  • \$\begingroup\$ It's much shorter if you don't do the bonus \$\endgroup\$ – 12Me21 Feb 11 '17 at 22:26
  • \$\begingroup\$ forcing a specific filename makes me feel like a freak. I'm beating half these answers anyway \$\endgroup\$ – snail_ Feb 12 '17 at 3:06
  • \$\begingroup\$ I think not turning off the LOAD dialog is much worse. \$\endgroup\$ – 12Me21 Feb 12 '17 at 3:15
  • \$\begingroup\$ It's actually shorter if you load it into slot 1 and use PRGEDIT commands to replace the first line (and add a linebreak after A=0B=1) And you also don't need A=0 the first time. \$\endgroup\$ – 12Me21 Feb 12 '17 at 3:33
0
\$\begingroup\$

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

(Has a trailing newline). It works with any valid filename.

\$\endgroup\$
0
\$\begingroup\$

Perl 6, 67 62 bytes (64 - 2)

say (1,1,*+*...*)[1];$*PROGRAM.&{.spurt: .slurp.&{S/\[<(\d+/{$/+1}/}}

say 0+1;$*PROGRAM.&{.spurt: .slurp.&{S/(\d+).(\d+)/$1+{$0+$1}/}}
\$\endgroup\$
0
\$\begingroup\$

Stacked, noncompeting, 65 (67 - 2) bytes

Some issues regarding file IO were fixed in the most recent series of commits. Thus, noncompeting.

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

Here's a link to the github.

Example execution

(I omitted the actual path for clarity.)

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

Explanation

How this works is by taking a pair of numbers to begin the sequence (2:> in this case is the integer range [0, 2), which is (0 1)), then performing the Fibonacci transformation on them, like so:

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

On each run, this transformation is executed on the top of stack. Then, the stack is pushed to the stack, duplicated, and its first member obtained (stack:0#). This item is then outputted, and is the desired Fibonacci number. repr then takes the representation of the stack and appends a newline. Then, the program is pushed to the stack, and split on newlines. Then, we take the last member (the last line), and append this to the aforementioned string. Finally, we push d0 (the file itself; think dollar sign 0 == $0.) and write to it.

\$\endgroup\$
0
\$\begingroup\$

Ruby, 68 bytes (70-2)

p$a=1+0
f=open$0,'r+'
s=f.read.sub /\d+.(\d+)/,"\\1+#$a"
f.seek 0
f<<s
\$\endgroup\$
0
\$\begingroup\$

Clojure, 209 204 195 bytes

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 bytes by switching to parse the numbers as a long instead of an integer, and removing a couple missed spaces.

-9 bytes by removing the space between the second number and (let...) (most expensive space ever!).

See the pregolfed code comments for a description.

Tested again, and it no longer throws unmatched bracket errors. It works up to 7540113804746346429, at which point it throws an integer overflow exception.

Also note, this assumes the source code is located at "./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
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.