Using a list comprehension to hide a for loop is kind of cheating given that the result produced by the comprehension is not the sorted list. But if you're going to do that, you might want to avoid building a list of None elements by performing the swaps in the condition rather than as the output value.
For example:
a = [1, 3, 2, 6, 5, 4]
[_ for n in range(len(a),1,-1) for i in range(n-1) if a[i]>a[i+1] and a.__setitem__(slice(i,i+2),a[i:i+2][::-1])]
Isolating the element swapping part, this would give:
swap = lambda(a,i):a.__setitem__(slice(i,i+2),a[i:i+2][::-1])
[_ for n in range(len(a),1,-1) for i in range(n-1) if a[i]>a[i+1] and swap(a,i)]
Which is no different from:
for n in range(len(a),1,-1):
for i in range(n-1):
if a[i]>a[i+1]:
swap(a,i) # same as a[i],a[i+1] = a[i+1],a[i]
The list comprehension is merely a different way to write the for loop and is not actually returning the sorted result.
What would be more in the spirit of a list comprehension is to actually return the sorted result without affecting the original list. You can do this using a temporary list within the comprehension to perform the element swapping and progressively return the position that is guaranteed to be at the right sorted index:
a = [1, 3, 2, 6, 5, 4]
s = [ b.pop(-1) for b in [list(a)] for n in range(len(a),0,-1) if not [_ for i in range(n-1) if b[i]<b[i+1] and b.__setitem__(slice(i,i+2),b[i:i+2][::-1])] ]
print(s) # [1, 2, 3, 4, 5, 6]
The approach is the same as before except that b
is used internally to manage swapping and return the sorted values. Because the guaranteed sorted position is always the last one in b
, the swapping condition was reversed so that internally b
is sorted in descending order which produces the output in ascending order when taking the last item at each iteration.
Note that all these solutions fail to implement the early exit condition that allows bubble sort to be very efficient on already sorted lists and lists where elements are near their sorted position (i.e. stop when no swaps in a pass). The number of iterations will always be N*(N+1)/2 no matter the original order of elements giving a time complexity of O(N^2) all the time instead of worst case.