pythonstringlistrewritingrhino3d

Rewriting System for Python L-System


I have the following working code for Python I produced. It´s a rewriting system based on Lindenmayer. The Output C is: +-LF+RFR+FL-F-+RF-LFL-FR+F+RF-LFL-FR+-F-LF+RFR+FL-+ which I can interpret to draw a space-filling curve. C is the starting letter and the process is performed n times.

C = 'L'
n = 2
Code = {ord('L'):'+RF-LFL-FR+',
ord('R'):'-LF+RFR+FL-'}

while n:
    C = C.translate(Code)
    n -=1

print C

Now I want, that the code is written automatically from a list. For example I have the list R=[['L', '+RF-LFL-FR+'], ['R', '-LF+RFR+FL-']] which should be automatically inserted in the code, so I can use it furthermore. The first element of every sub-list should be inserted in the ord() method and the second after the colon. Any suggestions?

I found a way via list comprehension. The List R is L=+RF-LFL-FR+, R=-LF+RFR+FL-. Now I ask if theres a more efficient way to get to the code?

R = ['L=+RF-LFL-FR+','R=-LF+RFR+FL-']
A = 'L'

for i in range(0,len(R)):
    R[i]=R[i].split('=')

print R

Start = []
Rule = []

for i in range(0,len(R)):
    Start.append(R[i][0])
    Rule.append(R[i][1])

#mapping via list comprehension
while n:
    A=''.join([Rule[Start.index(i)] if i in Start else i for i in A])
    n -=1

print A

Solution

  • Apparently this seems to be working for you. The code runs on python3.

    def fun1(n):
        axiom = 'L'
        rules = ['L=+RF-LFL-FR+','R=-LF+RFR+FL-']
    
        # Convert the rules into a translation table.
        rules = [ r.split('=') for r in rules ]
        table = dict((ord(key), value) for (key, value) in dict(rules).items())
    
        # Expand
        string = axiom
        for i in range(n):
            string = string.translate(table)
        return string
    

    Edit: I found a second approach that makes use of the built-in map function:

    def fun2(n):
        axiom = 'L'
        rules = ['L=+RF-LFL-FR+','R=-LF+RFR+FL-']
    
        # Convert the rules into a translation table.
        rules = [ r.split('=') for r in rules ]
        table = dict(rules)
        lookup = lambda c: table[c] if c in table else c
    
        string = axiom
        for i in range(n):
            # Map
            string = map(lookup, string)
            # "Reduce": from list of strings to string
            string = ''.join(string)
        return string
    

    Timing: To check the runtime, I executed the candidates for n=10, which leads to a resulting string with about 3'500'000 characters. Your implementation (without the print ops of course) I named fun3(n). The results I measured using the %timeit command in ipython.

    %timeit fun1(n=10)
    10 loops, best of 3: 143 ms per loop
    
    %timeit fun2(n=10)
    10 loops, best of 3: 195 ms per loop
    
    %timeit fun3(n=10)
    10 loops, best of 3: 152 ms per loop
    

    System: Python 3.5.2., MacBook Pro (Retina, 15-inch, Mid 2015), 2.8 GHz Intel Core i7.

    Summary: My first suggestion and your implementation perform about equally as fast, with a slight advantage on my version, particularly if it comes to readability. The map approach does not pay off.

    I also tried a fourth version where the output array is preallocated, however the code was getting involved and python's memory allocation logic outperformed my preallocation approach clearly by a factor of 2 in runtime. I did not further investigate on this.