[Tutor] Query: lists

2018-08-14 Thread Deepti K
 when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below
function, it picks up only 'xzz' and not 'xaa'

def front_x(words):
  # +++your code here+++
  a = []
  b = []
  for z in words:
if z.startswith('x'):
  words.remove(z)
  b.append(z)
  print 'z is', z
  print 'original', sorted(words)
  print 'new', sorted(b)
  print sorted(b) + sorted(words)

Thanks,
Deepti
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Query: lists

2018-08-14 Thread Alan Gauld via Tutor
On 14/08/18 09:11, Deepti K wrote:
>  when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below
> function, it picks up only 'xzz' and not 'xaa'

Correct because

> def front_x(words):
>   # +++your code here+++
>   a = []
>   b = []
>   for z in words:
> if z.startswith('x'):
>   words.remove(z)

You just changed the thing you are iterating over.
By removing an elem,ent the list got shorter so the
internal counter inside the for loop now points at
the next item - ie it skipped one.

As a general rule never modify the thing you are
iterating over with a for loop - use a copy or
change to a while loop instead.


>   b.append(z)
>   print 'z is', z
>   print 'original', sorted(words)

But it's not the original because you've removed
some items.

>   print 'new', sorted(b)
>   print sorted(b) + sorted(words)

But this should be the same as the original
(albeit almost sorted).

PS. Since you only modify 'b' and 'words' you
don't really need 'a'

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Query: lists

2018-08-14 Thread Cameron Simpson

On 14Aug2018 18:11, Deepti K  wrote:

when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below
function, it picks up only 'xzz' and not 'xaa'

def front_x(words):
 # +++your code here+++
 a = []
 b = []
 for z in words:
   if z.startswith('x'):
 words.remove(z)
 b.append(z)
 print 'z is', z
 print 'original', sorted(words)
 print 'new', sorted(b)
 print sorted(b) + sorted(words)


That is because you are making a common mistake which applies to almost any 
data structure, but is particularly easy with lists and loops: you are 
modifying the list _while_ iterating over it.


After you go:

 words.remove(z)

all the elements _after_ z (i.e. those after 'xzz' i.e. ['xaa']) are moved down 
the list.


In your particular case, that means that 'xaa' is now at index 3, and the next 
iteration of the loop would have picked up position 4. Therefore the loop 
doesn't get to see the value 'xaa'.


A "for" loop and almost anything that "iterates" over a data structure does not 
work by taking a copy of that structure ahead of time, and looping over the 
values. This is normal, because a data structure may be of any size - you do 
not want to "make a copy of all the values" by default - that can be 
arbitrarily expensive.


Instead, a for loop obtains an "iterator" of what you ask it to loop over. The 
iterator for a list effectively has a reference to the list (in order to obtain 
the values) and a notion of where in the list it is up to (i.e. a list index, a 
counter starting at 0 for the first element and incrementing until it exceeds 
the length of the list).


So when you run "for z in words", the iterator is up to index 3 when you reach 
"xzz". So z[3] == "xzz". After you remove "xzz", z[3] == "xaa" and in this case 
there is no longer a z[4] at all because the list is shortened. So the next 
loop iteration never inspects that value. Even if the list had more value, the 
loop would still skip the "xaa" value.


You should perhaps ask yourself: why am I removing values from "words"?

If you're just trying to obtain the values starting with "x" you do not need to 
modify words because you're already collecting the values you want in "b".


If you're trying to partition words into values starting with "x" and values 
not starting with "x", you're better off making a separate collection for the 
"not starting with x" values. And that has me wondering what the list "b" in 
your code was for originally.


As a matter of principle, functions that "compute a value" (in your case, a 
list of the values starting with "x") should try not to modify what they are 
given as parameters. When you pass values to Python functions, you are passing 
a reference, not a new copy. If a function modifies that reference's _content_, 
as you do when you go "words.move(z)", you're modifying the original.


Try running this code:

 my_words = ['bbb', 'ccc', 'axx', 'xzz', 'xaa']
 print 'words before =", my_words
 front_x(my_words)
 print 'words after =", my_words

You will find that "my_words" has been modified. This is called a "side 
effect", where calling a function affects something outside it. It is usually 
undesirable.


Cheers,
Cameron Simpson 
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Query: lists

2018-08-14 Thread Peter Otten
Alan Gauld via Tutor wrote:

> On 14/08/18 09:11, Deepti K wrote:
>>  when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below
>> function, it picks up only 'xzz' and not 'xaa'
> 
> Correct because
> 
>> def front_x(words):
>>   # +++your code here+++
>>   a = []
>>   b = []
>>   for z in words:
>> if z.startswith('x'):
>>   words.remove(z)
> 
> You just changed the thing you are iterating over.
> By removing an elem,ent the list got shorter so the
> internal counter inside the for loop now points at
> the next item - ie it skipped one.
> 
> As a general rule never modify the thing you are
> iterating over with a for loop - use a copy or
> change to a while loop instead.
> 
> 
>>   b.append(z)
>>   print 'z is', z
>>   print 'original', sorted(words)
> 
> But it's not the original because you've removed
> some items.
> 
>>   print 'new', sorted(b)
>>   print sorted(b) + sorted(words)
> 
> But this should be the same as the original
> (albeit almost sorted).
> 
> PS. Since you only modify 'b' and 'words' you
> don't really need 'a'

For a simple solution you do need a and b: leave words unchanged, append 
words starting with "x" to a and words not starting with "x" to b.

Someone familiar with Python might do it with a sort key instead:

>>> sorted(['bbb', 'ccc', 'axx', 'xzz', 'xaa'],
... key=lambda s: not s.startswith("x"))
['xzz', 'xaa', 'bbb', 'ccc', 'axx']

If you want ['xaa', 'xzz', 'axx', 'bbb', 'ccc'] as the result
you can achieve that by sorting twice (Python's sorting is "stable") or by 
tweaking the key function.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Query: lists

2018-08-14 Thread Deepti K
Thanks all. This is very helpful. I am new to Python :)

Sent from my iPhone

> On 15 Aug 2018, at 8:16 am, Peter Otten <__pete...@web.de> wrote:
> 
> Alan Gauld via Tutor wrote:
> 
>>> On 14/08/18 09:11, Deepti K wrote:
>>> when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below
>>> function, it picks up only 'xzz' and not 'xaa'
>> 
>> Correct because
>> 
>>> def front_x(words):
>>>  # +++your code here+++
>>>  a = []
>>>  b = []
>>>  for z in words:
>>>if z.startswith('x'):
>>>  words.remove(z)
>> 
>> You just changed the thing you are iterating over.
>> By removing an elem,ent the list got shorter so the
>> internal counter inside the for loop now points at
>> the next item - ie it skipped one.
>> 
>> As a general rule never modify the thing you are
>> iterating over with a for loop - use a copy or
>> change to a while loop instead.
>> 
>> 
>>>  b.append(z)
>>>  print 'z is', z
>>>  print 'original', sorted(words)
>> 
>> But it's not the original because you've removed
>> some items.
>> 
>>>  print 'new', sorted(b)
>>>  print sorted(b) + sorted(words)
>> 
>> But this should be the same as the original
>> (albeit almost sorted).
>> 
>> PS. Since you only modify 'b' and 'words' you
>> don't really need 'a'
> 
> For a simple solution you do need a and b: leave words unchanged, append 
> words starting with "x" to a and words not starting with "x" to b.
> 
> Someone familiar with Python might do it with a sort key instead:
> 
 sorted(['bbb', 'ccc', 'axx', 'xzz', 'xaa'],
> ... key=lambda s: not s.startswith("x"))
> ['xzz', 'xaa', 'bbb', 'ccc', 'axx']
> 
> If you want ['xaa', 'xzz', 'axx', 'bbb', 'ccc'] as the result
> you can achieve that by sorting twice (Python's sorting is "stable") or by 
> tweaking the key function.
> 
> ___
> Tutor maillist  -  Tutor@python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Query: lists

2018-08-14 Thread Nitin Madhok
Deepti,

What you’re seeing happens because you are making changes (words.remove(z)) to 
the list while you are iterating over it (for z in words). If your goal is to 
print the original words, removed words and original words without removed 
words, you could do something like this using sets:

words = ['bbb', 'ccc', 'axx', 'xzz', 'xaa']

def front_x(words):
 # +++your code here+++
 removed_words = []
 for word in words:
   if word.startswith('x'):
 removed_words.append(word)
 print 'word is', word
 print 'original', sorted(words)
 print 'new', sorted(removed_words)
 print sorted(list(set(removed_words + words)))

If you also wanted to get the original words after removing the removed_words, 
you could print them like this:
print 'original - new', list(set(words) - set(removed_words))

Or you could even use list comprehension like this:
print 'original - new', [word for word in words if word not in removed_words]

--

Thanks,
Nitin Madhok
Clemson University

CONFIDENTIALITY NOTICE
This e-mail, and any attachments thereto, is intended only for use by the 
addressee(s) named herein and may contain privileged and/or confidential 
information. If you are not the intended recipient of this e-mail, any 
dissemination, distribution or copying of this e-mail, and any attachments 
thereto, is strictly prohibited. If you have received this e-mail in error, 
please immediately notify the sender by e-mail or telephone and permanently 
delete all copies of this e-mail and any attachments.

> On Aug 14, 2018, at 4:11 AM, Deepti K  wrote:
> 
> when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below
> function, it picks up only 'xzz' and not 'xaa'
> 
> def front_x(words):
>  # +++your code here+++
>  a = []
>  b = []
>  for z in words:
>if z.startswith('x'):
>  words.remove(z)
>  b.append(z)
>  print 'z is', z
>  print 'original', sorted(words)
>  print 'new', sorted(b)
>  print sorted(b) + sorted(words)
> 
> Thanks,
> Deepti
> ___
> Tutor maillist  -  Tutor@python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Query: lists

2018-08-14 Thread Alan Gauld via Tutor
On 14/08/18 23:16, Peter Otten wrote:

> For a simple solution you do need a and b: leave words unchanged, append 
> words starting with "x" to a and words not starting with "x" to b.
> 
> Someone familiar with Python might do it with a sort key instead:

Or, for one definition of simple, a list comprehension?

filtered_list = [word for word in words if not word.startswith('x')]

Of course it doesn't retain the deleted words if that is important.
Or if you only want the deleted words simply remove the 'not'.

If you need all three results (original, filtered and removed)
then you need the original 'a' and 'b' lists as well as the
original 'words'.

It all depends on what exactly you need as an end result.

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Query: lists

2018-08-14 Thread Alan Gauld via Tutor
On 14/08/18 22:38, Cameron Simpson wrote:

> If you're trying to partition words into values starting with "x" and values 
> not starting with "x", you're better off making a separate collection for the 
> "not starting with x" values. And that has me wondering what the list "b" in 
> your code was for originally.

And further to Cameron's point this demonstrates why choosing
meaningful variable names (rather than single letters) is so
important. If a,b and z had been names expressing their purpose
we would be better able to guess at your intentions and
ultimate goal. But with single letters we have no real clue.

> As a matter of principle, functions that "compute a value" (in your case, a 
> list of the values starting with "x") should try not to modify what they are 
> given as parameters. When you pass values to Python functions, you are 
> passing 
> a reference, not a new copy. If a function modifies that reference's 
> _content_, 
> as you do when you go "words.move(z)", you're modifying the original.

It's also good if functions that compute a value *return* that
value rather than (or as well as) print it. The act of returning
something also helps to clarify the functions objective. With
multiple print statements we are not quite sure which line of
output is the most important.


-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor