Indent your code according to Fibonacci

1

1

Okay, so I saw this in a meme and decided it'd be the perfect code golf challenge:

I prefer to increase the side of my indents according to the Fibonacci sequence:

class Kls
 def method
  t = Time.now
  if t.hour >= 12
    puts "It's afternoon"
    if t.hour == 18
       puts "It's time for your meal of:"
       %w(bread sausage brocolli milk).each do |food|
            "Some #{food}"
       end
    end
  else
    puts "It's not afternoon"
  end
 end
end 

This means each line is indented by the sum of the spaces indented in the previous indentation-level. The first indention-level is indented by one space, then subsequent indention-levels are indented by 1, 2, 3, 5, 8, 13, 21, etc additional spaces.

The challenge

Given a multi-line string with multiples of 2 spaces as indentation, convert it to a Fibonacci indented multi-line string instead.
This means every line with 0 indented spaces will remain 0 spaces; every line with 2 indented spaces will become 1 instead (sum of the first two Fibonacci numbers 0+1 = 1); every line with 4 indented spaces will becomes 2 instead (sum of the first three Fibonacci numbers 0+1+1 = 2); etc. So the number of indentations can be calculated as follows:

  • Get the current amount of leading spaces (guaranteed to be a multiple of 2, so [0,2,4,6,8,...])
  • Divide this by 2, so we have our 0-indexed numbers (let's call it n)
  • Get the first [0,n] (0-indexed) Fibonacci numbers
  • Sum that sub-sequence together, and use that amount of leading spaces instead in the output

For example, for the output above, the input would have been:

class Kls
  def method
    t = Time.now
    if t.hour >= 12
      puts "It's afternoon"
      if t.hour == 18
        puts "It's time for your meal of:"
        %w(bread sausage brocolli milk).each do |food|
          "Some #{food}"
        end
      end
    else
      puts "It's not afternoon"
    end
  end 
end

If we take a look at the line containing "Some #{food}":

In the input it has 10 amount of leading spaces, so n will be 10/2 = 5. The first [0,5] Fibonacci numbers are: [0,1,1,2,3,5]. When we take the sum of that list (0+1+1+2+3+5) the new amount of leading spaces in the output would be 12.

The amount of indented spaces in the output for every 2 indented spaces in the input will be [0, 1, 2, 4, 7, 12, 20, 33, 54, 88, ...] A000071 on oeis.org (except for the first 0).

Challenge rules:

  • The space-indentation of the input is guaranteed to be multiples of 2 ([0,2,4,6,8,...])
  • The input is guaranteed to not contain more than 1 consecutive space elsewhere (except for the leading indentation of course)
  • Use whatever language you like
  • Please include a link to an online interpreter
  • Input should be a multi-line string (so no, string-lists/arrays aren't allowed)
  • Output should also be a multi-line string (either printed or returned as string)
  • Your method should work for deeper-indented Fibonacci indented multi-line strings than the example
  • This is , so try to do it in the fewest bytes possible.

Additional test cases:

Input:

this
  is
    one
      of
        the
          tests
            !

Output:

this
 is
  one
    of
       the
            tests
                    !

Input:

      And
  how
               about
        this ?

Output:

    And
 how
                                 about
       this ?

Input:

                    20 spaces?

Output:

                                                                                                                                               20 spaces?

AJFaraday

Posted 2018-08-22T11:07:03.113

Reputation: 10 466

1What if there's 2 or more spaces in the code that aren't part of the indentation? – Blue – 2018-08-22T11:21:06.987

3@AJFaraday I took the liberty of editing your challenge so it can be re-opened. The challenge in general is a nice idea and I personally like it; it was just not properly explained. If you see anything incorrect in what I've edited, let me know. – Kevin Cruijssen – 2018-08-23T09:19:05.863

Answers

4

05AB1E (legacy), 22 21 bytes

|εDgsðÛ©g-;LÅfOð*®J}»

Try it online.

21 bytes alternative using the formula \$Fib(\frac{n}{2}+2)-1\$ instead of \$\sum_{i=1}^{i=\frac{n}{2}}{Fib(i)}\$ :
(Credit to @JonathanAllan's Jelly answer for this formula.)

|εDgsðÛ©g-;ÌÅf<ð*®J}»

Try it online.

Explanation:

|                        # Take the input split by newlines
 ε                  }    # Map each line to:
  Dg                     #  Duplicate the line and take its length
    s                    #  Swap so the duplicated line is at the top of the stack again
     ðÛ                  #  Remove all leading spaces
       ©                 #  Store the result in the register (without popping)
        g                #  Take its length as well
         -               #  Subtract both lengths from each other
          ;              #  Halve it, because they are multiples of 2 indented spaces
           L             #  Create a list in the range [1, n]
            Åf           #  Get the i'th Fibonacci number for each
              O          #  And sum them together
               ð*        #  Have that many space characters
                 ®J      #  And join them with the saved string from the register
                    »    # And finally print the mapped list with new-lines

Kevin Cruijssen

Posted 2018-08-22T11:07:03.113

Reputation: 67 575

3

Python 2, 111 109 100 95 bytes

g=lambda n:n<1or g(n-2)+g(n-4)
def f(I):
 for l in I:L=l.lstrip();print~-g(len(l)-len(L))*' '+L

Try it online!


Saved:

  • -5 bytes, thanks to ovs.

TFeld

Posted 2018-08-22T11:07:03.113

Reputation: 19 246

1g can be g=lambda n:n<1or g(n-2)+g(n-4). – ovs – 2018-08-22T11:53:44.813

and you can use strip instead of lstrip as trailing whitespace is insignificant. – ovs – 2018-08-22T11:56:04.507

@ovs But it would affect len(l)-len(L). – user202729 – 2018-08-22T11:58:30.743

@ovs, Thanks :) – TFeld – 2018-08-22T12:16:29.747

3

Husk, 23 bytes

mȯF+M(R' !∫Θİf→½L)↕=' ¶

Try it online!

Explanation

m(F+M(R' !∫Θİf→½L)↕=' )¶  -- full program
                       ¶  -- lines
m(                    )   -- map
                  ↕='     -- | split on first non-space
    M(           )        -- | with the spaces
                L         -- | | length
               ½          -- | | halve
              →           -- | | increment
            İf            -- | | Fibonacci numbers: [1,1,2,3,5..]
           Θ              -- | | prepend 0: [0,1,1,2,3,5..]
          ∫               -- | | cumulative sums: [0,1,2,4,7,12..]
         !                -- | | 1-based index
      R'                  -- | | replicate that many spaces
  F+                      -- | join

ბიმო

Posted 2018-08-22T11:07:03.113

Reputation: 15 345

1This answer appears to have three levels of indent with only 1 space, then continuing in Fibonacci. – AJFaraday – 2018-08-22T12:29:14.703

I had the same mistake at first. Instead of having the indexed Fibonacci amount of spaces in the result, it's actually the sum of the first [1,n] amount of Fibonacci numbers. So instead of [0,1,1,2,3,5,8,...] indented spaces, it's (sum_each([0,[0,1],[0,1,1],[0,1,1,2],[0,1,1,2,3],[0,1,1,2,3,5],[0,1,1,2,3,5,8],...]) -> [0,1,2,4,7,12,20,...] indented spaces instead. – Kevin Cruijssen – 2018-08-22T12:35:43.537

1@KevinCruijssen: I was able to fix it w/o additional bytes, thanks a lot! – ბიმო – 2018-08-22T13:21:40.237

2

Jelly, 20 bytes

Ỵµ=⁶i0‘H‘ÆḞ’⁶ẋ;t⁶$)Y

Try it online!


Explanation

Let \$x\$ be the 1-indexing index of the first non-whitespace character (\$1,3,5,7,\dots\$).

Then the new indent is

$$F_{(x+1)/2+1}-1$$

where \$F\$ is the Fibonacci function.

user202729

Posted 2018-08-22T11:07:03.113

Reputation: 14 620

Save 1 from both with Ỵµ...)Y – Jonathan Allan – 2018-08-22T14:24:11.687

2

Retina 0.8.2, 37 bytes

m`^
 	 	
+`( +	)( +)	  
$2$1$1
 	 *	

Try it online! Warning: Lots of white space. Explanation:

m`^
 	 	

Prefix each line with space tab space tab. These keep track of the previous and current Fibonacci number.

+`( +   )( +)     
$2$1$1

For each two spaces of the original indentation, compute the next Fibonacci number by summing the previous two.

 	 *	

Subtract 1 from the result and delete the previous Fibonacci number.

Neil

Posted 2018-08-22T11:07:03.113

Reputation: 95 035

2

Japt v2.0a0, 20 19 bytes

r/^ +/m@ÓMgXÊz ÄÄ)ç

Try it


Explanation

r                        :Replace
 /^ +/m                  :Global, multiline regular expression matching one or more spaces at the start of each line
       _                 :Pass each match through a function
        Ê                :  Length
         z               :  (Floor) divide by 2
           õ             :  Range [0,result]
              @          :  Map each X
               MgX       :    Xth Fibonacci number
                  Ã      :  End mapping
             x           :  Reduce by addition
                   ç     :  Repeat <space> that many times

Shaggy

Posted 2018-08-22T11:07:03.113

Reputation: 24 623

2

Jelly, 17 bytes

Ỵµwt©⁶$HÆḞ€⁶ẋ;®)Y

A full program which prints the result.

Try it online!

How?

The indentations required for a line of the output is \$Fib(\frac{s}{2}+2)-1\$ where \$s\$ is the number of spaces in the line of the input, and:

$$Fib(\frac{s}{2}+2)-1=\sum_{i=1}^{i=\frac{s}{2}}{Fib(i)}$$

(\$i\$ could also start at \$0\$ since \$Fib(0)=0\$)

Ỵµwt©⁶$HÆḞ€⁶ẋ;®)Y - Main Link: list of characters
Ỵ                 - split at newlines
 µ             )  - for each line:
      $           -   last 2 links as a monad:
     ⁶            -     literal space character
   t              -     trim from both sides
    ©             -     (copy to register)
  w               -   first index of that substring in the line
       H          -   halve (this is (s+1)/2)
        ÆḞ€       -   Fibonacci of €ach (using an implicit range [1,2,...,s/2]
                  -                      where the (s+1)/2 is floored to s/2)
           ⁶      -   literal space character
            ẋ     -   repeat (vectorises) (e.g. [1,1,2,3,5] -> [[' '],[' '],[' ',' '],[' ',' ',' '],[' ',' ',' ',' ',' ']]
              ®   -   recall from register
             ;    -   concatenate
                Y - join with newline characters 
                  - implicit print
                  -  ...smashes, so e.g. [[' '],[' '],[' ',' '],['x']] is just "    x"

Jonathan Allan

Posted 2018-08-22T11:07:03.113

Reputation: 67 804

2

Java 11, 150 bytes

s->{for(var l:s.split("\n"))System.out.println(" ".repeat(f(l.length()-(l=l.stripLeading()).length())-1)+l);};int f(int n){return n<1?1:f(n-2)+f(n-4);}

Try it online (NOTE: Both String.repeat(int) and String.stripLeading() are emulated as repeat(String,int) and stripLeadingE(String) respectively, because Java 11 isn't on TIO yet.)

Explanation:

s->{                   // Method with String parameter and no return-type
  for(var l:s.split("\n"))
                       //  Loop over the lines of the input:
    System.out.println(//   Print with trailing new-line:
      " ".repeat(      //    Repeat a space this many times:
           f(          //     Call the separated (recursive) method `f` with the parameter:
             l.length()//      The length of the line,
             -(l=l.stripLeading()).length()
                       //      minus the length of the line without leading spaces
           )-1         //     minus 1
      )+l);};          //    And append the line without leading spaces

int f(int n){          // Separated method with integer as both parameter and return-type
  return n<1?          //  If the input is 0 or lower:
          1            //   Return 1
         :             //  Else:
          f(n-2)+f(n-4);}
                       //   Return a recursive call to `n-2` plus a recursive call to `n-4`

Kevin Cruijssen

Posted 2018-08-22T11:07:03.113

Reputation: 67 575

1

JavaScript (Node.js), 87 81 bytes

x=>x.replace(/^ */gm,s=>" ".repeat((g=(n,a=b=1)=>n?g(n-2,b,b+=a):b-1)(s.length)))

Try it online!

For small indent levels (up to 15), 61 bytes using approximation:

x=>x.replace(/^ */gm,s=>" ".repeat(1.272**s.length*1.171-.5))

Use 1.27202 ** s.length * 1.17082 - .5 for indents up to 21 levels

Shieru Asakoto

Posted 2018-08-22T11:07:03.113

Reputation: 4 445

1

Perl 6, 62 bytes

*.trans(/[\n|^]' '+/=>{"\n"~' 'x(1,1,*+*...*)[$/.ords/2+1]-1})

Try it online!

Jo King

Posted 2018-08-22T11:07:03.113

Reputation: 38 234