0

I want to know if there is a way to do the code above as an in-place list comprehension or using a map() (just the for loop):

s = [''] * n
s[0:k] = ['X'] * k
for i in range(k,m): 
    s[i] = foo(s[i-k:i]) 

If I do:

s = [''] * n
s[0:k] = ['X'] * k
s = [foo(s[i-k:i]) for i in range(k,m)]

the s will not be updated correctly.

ViniciusArruda
  • 970
  • 12
  • 27
  • 9
    It's already incomprehensible enough – wim Nov 29 '16 at 00:04
  • you realize `i-k` will have negative values in the beginning since `k > 0` and `i >= 0`, what will cause the indices `[i-k:i]` to have no meaning (empty list) – Uriel Nov 29 '16 at 00:04
  • @UrielEli Sorry, I edited – ViniciusArruda Nov 29 '16 at 00:05
  • I think list comprehensions and `map()` both return new lists, so neither are what you would want to do for in-place modification of your existing list. – beeftendon Nov 29 '16 at 00:05
  • Comprehensions are functional constructs. They always return a new data-structure. You can update your own with that new one, but that muddies the water. Stick to imperative constructs (loops with append) if you are going to be mutating a data structure, stick with functional constructs if you are going to be programming in the functional style. – juanpa.arrivillaga Nov 29 '16 at 00:10
  • You can simplify the initialization with `s = ['X' if i < k else '' for i in range(n)]`, but I don't see a way to lose the `for` loop. – Mark Ransom Nov 29 '16 at 00:12

2 Answers2

0

The killer case here is that if m > 2*k, we get foo taking as input something it produced as output on a prior call. That means that we can't simply evaluate s statically and iterate blithely over the known elements -- the elements after s[max(k, n)] are unknown at the initial evaluation, so feeding one of those to foo is a dynamic quantity.

Imagine that foo returns something dependent on the initial value and something random; it's easy enough to see that the propagation from one part of the list to another doesn't work statically. For instance, have foo append a random hex digit, and let's try both ideas in the original posting:

import random
def foo(chr_list):
    inventory = "0123456789abcdef"
    return chr_list[0] + random.choice(inventory)

k = 3
m = 10
n = 16
s = [''] * n
s[0:k] = ['X'] * k

print "1 s init", s 
for i in range(k,m): 
    s[i] = foo(s[i-k:i])
print "1 s done", s
print

s = [''] * n
s[0:k] = ['X'] * k
print "2 s init", s
s = [foo(s[i-k:i]) for i in range(k,m)]
print "2 s done", s

Output:

1 s init ['X', 'X', 'X', '', '', '', '', '', '', '', '', '', '', '', '', '']
1 s done ['X', 'X', 'X', 'X7', 'Xd', 'X9', 'X74', 'Xd6', 'X9d', 'X741', '', '', '', '', '', '']

2 s init ['X', 'X', 'X', '', '', '', '', '', '', '', '', '', '', '', '', '']
2 s done ['X8', 'X2', 'X9', 'f', 'd', '0', '7']

In summary, I don't think it's worth putting this into a one-liner.

Prune
  • 76,765
  • 14
  • 60
  • 81
0

As the comments mention, list comprehension always create a new list. Your original list s will be gone if you directly assign result of list comprehension into it.

You can replace slice of original list with the result of list comprehension as below

# compact initialization into 1 line
s = ['X']*k + [''] * (n-k)
# replace slice of list s from k to m
s[k:m] = [foo(s[i-k:i]) for i in range(k,m)]
Skycc
  • 3,496
  • 1
  • 12
  • 18