Reputation: 331
The reason I am asking is that in incorporates various data structures, dict, list, string.
bffer = ""
dict_size = 128
for character in string:
appnd_bffer = bffer + character
if appnd_bffer in codebook:
bffer = appnd_bffer
else:
output_list.append(codebook[bffer])
codebook[appnd_bffer] = dict_size
dict_size += 1
bffer = character
I am new to list comprehensions so I could really use an explanation if it is possible. Cheers.
Upvotes: 1
Views: 218
Reputation: 55479
Just to give you an idea of why it's a really bad idea to try and stuff your LZW compressor's loop into a list comprehension, I've actually gone ahead and written some crazy code that does just that.
It uses some rather dodgy tricks, and it won't work on Python 3 because in Python 3 a list comprehension runs in its own scope; in Python 2 a list comprehension runs in the scope of the surrounding code. And that means my trick of passing the original buffer into the the list comp won't work in Python 3.
Firstly, here's your original code wrapped in a function, with a couple of variable name changes, and a few added extras to make it a runnable, testable example.
from __future__ import print_function
def lzw_compress_Boa(data):
dict_size = 128
codebook = {chr(i): i for i in range(dict_size)}
output_list = []
oldbuff = ""
for ch in data:
newbuff = oldbuff + ch
if newbuff in codebook:
oldbuff = newbuff
else:
output_list.append(codebook[oldbuff])
codebook[newbuff] = dict_size
dict_size += 1
oldbuff = ch
return output_list
data = 'this data is this data'
output_list = lzw_compress_Boa(data)
print(output_list)
print(len(data), '->', len(output_list))
output
[116, 104, 105, 115, 32, 100, 97, 116, 97, 32, 130, 32, 128, 138, 133]
22 -> 15
We don't actually need to keep the codebook's size in a separate variable. Like all of Python's built-in container types, a dict keeps track of its size, and we can use the len()
function to get it. So we can replace these 2 lines:
codebook[newbuff] = dict_size
dict_size += 1
with this line:
codebook[newbuff] = len(codebook)
Now here's the crazy version that uses a list comprehension. Remember kids, please don't try this at home! :)
def lzw_compress_crazy(data):
dict_size = 128
codebook = {chr(i): i for i in range(dict_size)}
def magic(oldbuff, ch):
newbuff = oldbuff + ch
if newbuff in codebook:
return [(newbuff, None)]
else:
codebook[newbuff] = len(codebook)
return [(ch, codebook[oldbuff])]
oldbuff = ""
return [result for ch in data
for oldbuff, result in magic(oldbuff, ch) if result]
Note that this version is not only harder to read than the first one I posted, it's also less efficient. And as I said at the start, it's less portable, and it uses some totally dodgy tricks that should never be used in serious code.
List comprehensions are cool, and once you're used to them they can make your code more concise, which can aid in readability, as long as you don't try to do too much in them. A list comp is slightly more efficient than equivalent code using .append
in a "traditional" style for
loop, but they aren't magic, and using an incomprehensible list comprehension instead of a a nice clear readable traditional for
loop is not Pythonic.
Upvotes: 5
Reputation: 1122552
The loop currently depends on being able to assign to bffer
. Because assignment is a statement, and list comprehensions only can contain expressions, converting this to a list comprehension would require a lot of hard-to-follow trickery with mutable objects.
As such, converting this to a list comprehension would result in an unreadable mess, without any clear benefits (any speed benefits from removing the list.append()
call will be offset by the mutable object manipulations).
Upvotes: 8
Reputation: 1429
No, this cannot be made into a list comprehension because it contains expressions other than the ones that make up the list (e.g. dict_size += 1
). Those expressions just have no place to go in a list comprehension, since the only purpose of a comprehension is to create that object. What might help you is to create a separate function containing the logic for a single iteration of the loop, then use that function in a loop.
Upvotes: 2