Sąraše ištrinti pasikartojančias reikšmes

J
  • 29 Geg '11

Tarkim, noriu sąraše palikti tik po vieną reikšmę, ištrindamas pasikartojančias:

raides = ['a', 'c', 'a', 'a', 'k', 'o', 'l', 'b', 't', 'a', 'f', 'k', 'y', 'a', 'b', 'm', 'a']
for j in raides:
    if raides.count(j) > 1:
        raides.remove(j)
print raides
# lyg ir veikia:
>>>['c', 'o', 'l', 't', 'f', 'k', 'y', 'b', 'm', 'a']

Bet jeigu sąraše skaičiai, nebeveikia. Ištrina tik kas antrą pasikartojantį skaičių ar kažkas panašaus:

skaiciai = [1, 2, 4, 1, 2, 5, 1, 6, 8, 1, 2, 5, 2, 3, 4, 8, 3, 5]
for i in skaiciai:    
    if skaiciai.count(i) >1:        
        skaiciai.remove(i)
print skaiciai
>>>[5, 1, 6, 1, 5, 2, 4, 8, 3, 5]

Skaitinėju tekstus pradinukams, ir kai jau pradedu galvoti, kad kažką išmokau, Pythonas pasišaipo:). Ne tiek įdomu kaip reikėtų spręsti tokią užduotį, bet norėtųsi sužinot kas čia daros su tais skaičiais.
Būčiau dėkingas ir už pasiuntimą į konkrečią vietą - t.y. kur pasiskaitęs suprasčiau kame čia bėda:)

S
  • 29 Geg '11

Savo pavyzdžiuose modifikuoji sąrašą, per kurį suki ciklą, tai reiškia, sąrašas trumpėja, o sąrašo kursorius elementus ima iš eilės ir praleidžia tuos, kurie pasislinko per vieną atgal dėl einamojo elemento ištrynimo.

Trumpiau tariant, geriau niekada nekeisk sąrašo, per kurį suki ciklą, nebent žinai ką darai.

Jie nori pašalinti besidubliuojančias reikšmes, reikia naudoti aibes (set()):

>>> raides = ['a', 'c', 'a', 'a', 'k', 'o', 'l',
...           'b', 't', 'a', 'f', 'k', 'y', 'a',
...           'b', 'm', 'a']
>>> set(raides)
set(['a', 'c', 'b', 'f', 'k', 'm', 'l', 'o', 't', 'y'])
>>> skaiciai = [1, 2, 4, 1, 2, 5, 1, 6, 8, 1, 2, 5,
...             2, 3, 4, 8, 3, 5]
>>> set(skaiciai)
set([1, 2, 3, 4, 5, 6, 8])

Jei dėl kokių nors priežasčių netinka set(), visada rekomenduojamas būdas apdoroti sąrašus yra kuriant kitą sąrašo kopiją:

>>> skaiciai = [1, 2, 4, 1, 2, 5, 1, 6, 8, 1, 2, 5,
...             2, 3, 4, 8, 3, 5]
>>> unikalus = []
>>> for i in skaiciai:
...     if i not in unikalus:
...         unikalus.append(i)
>>> print(unikalus)
[1, 2, 4, 5, 6, 8, 3]
S
  • 29 Geg '11

Dar vienas variantas dėl sąrašų apdorojimo yra taip vadinami generatoriai.

Tavo pavyzdį su generatoriais būtų galima perrašyti taip:

>>> def unique(l):
...     unique = []
...     for i in l:
...         if i not in unique:
...             unique.append(i)
...             yield i
>>> skaiciai = [1, 2, 4, 1, 2, 5, 1, 6, 8, 1, 2, 5,
...             2, 3, 4, 8, 3, 5]
>>> print(list(unique(skaiciai)))
[1, 2, 4, 5, 6, 8, 3]
J
  • 29 Geg '11

Labai dėkui už vargą.
Iš pradžių - su skaičiais - buvau apie tai pagalvojęs. Bet kai pabandžiau su raidėm, tada jau ta gyvatė pradėjo ėst smegenis.
Juk turėtų ištrint kas antrą, bet

raides = ['a', 'a', 'a', 'a', 'a', 'a', 'lept', 'k', 'a', 'a', 'a', 'a', 'a']
>>> ['lept', 'k', 'a', 'a', 'a', 'a', 'a']  # istrina visas pirmas 6 "a"
S
  • 31 Geg '11

Tokiose situacijos visada galima pasitelkti debugerį ir išsiaiškinti, kame problema.

Debugerį galima paleisti į kodą įrašius tokią eilutę:

import pdb; pdb.set_trace()

Tarkim pilnas kodo pavyzdys būtų toks:

raides = ['a', 'a', 'a', 'a', 'a', 'a', 'lept',
          'k', 'a', 'a', 'a', 'a', 'a']
import pdb; pdb.set_trace()
for j in raides:
    if raides.count(j) > 1:
        raides.remove(j)

Tada debuginimas vyksta taip:

> python skaiciai.py
> /tmp/skaiciai.py(4)<module>()
-> for j in raides:
(Pdb) help

Documented commands (type help <topic>):
========================================
EOF    bt         cont      enable  jump  pp       run      unt
a      c          continue  exit    l     q        s        until
alias  cl         d         h       list  quit     step     up
args   clear      debug     help    n     r        tbreak   w
b      commands   disable   ignore  next  restart  u        whatis
break  condition  down      j       p     return   unalias  where

Miscellaneous help topics:
==========================
exec  pdb

Undocumented commands:
======================
retval  rv

(Pdb) help p
p expression
Print the value of the expression.
(Pdb) p raides
['a', 'a', 'a', 'a', 'a', 'a', 'lept', 'k', 'a', 'a', 'a', 'a', 'a']
(Pdb) l
  1     raides = ['a', 'a', 'a', 'a', 'a', 'a', 'lept',
  2               'k', 'a', 'a', 'a', 'a', 'a']
  3     import pdb; pdb.set_trace()
  4  -> for j in raides:
  5         if raides.count(j) > 1:
  6             raides.remove(j)
[EOF]
(Pdb) n
> /tmp/skaiciai.py(5)<module>()
-> if raides.count(j) > 1:
(Pdb) p j
'a'
(Pdb) l
  1     raides = ['a', 'a', 'a', 'a', 'a', 'a', 'lept',
  2               'k', 'a', 'a', 'a', 'a', 'a']
  3     import pdb; pdb.set_trace()
  4     for j in raides:
  5  ->     if raides.count(j) > 1:
  6             raides.remove(j)
[EOF]
(Pdb) tbreak 5, j != 'a'
Breakpoint 1 at /tmp/skaiciai.py:5
(Pdb) c
Deleted breakpoint 1
> /tmp/skaiciai.py(5)<module>()
-> if raides.count(j) > 1:
(Pdb) p j, raides
('lept', ['a', 'a', 'a', 'lept', 'k', 'a', 'a', 'a', 'a', 'a'])
(Pdb) b 5
Breakpoint 2 at /tmp/skaiciai.py:5
(Pdb) c
> /tmp/skaiciai.py(5)<module>()
-> if raides.count(j) > 1:
(Pdb) c
> /tmp/skaiciai.py(5)<module>()
-> if raides.count(j) > 1:
(Pdb) p j, raides
('a', ['a', 'a', 'a', 'lept', 'k', 'a', 'a', 'a', 'a', 'a'])
(Pdb) c
> /tmp/skaiciai.py(5)<module>()
-> if raides.count(j) > 1:
(Pdb) p j, raides
('a', ['a', 'a', 'lept', 'k', 'a', 'a', 'a', 'a', 'a'])

Iš viso šito matosi labai aiškus atsakymas, cikle sąrašas modifikuojamas, todėl kiekvieną kartą ištrynus vieną elementą sąrašas sutrumpėja, visi elementai pasislenka į pradžią, todėl visą laiką ištrynus vienas elementas yra praleidžiamas. Todėl prasisukus per visus 6 pasikartojančius 'a' sąrašo elementus, trys ištrinami, trys lieka. Toliau situacija vėl kartojasi, sąrašo pabaigoje yra 5 pasikartojantys 'a' elementai, iš kurių bus ištrintas tik kas antras, todėl nuo pradžios vėl nutrinami trys 'a' elementai, o visi kiti lieka.

Dar kartą pasikartosiu negalima keisti sąrašo, per kurį sukamas ciklas. Keisti gali tik tuo atveju, jei tiksliai žinai ką darai, bet net ir tuo atveju, kodas bus sunkiai suprantamas.

J
  • 31 Geg '11

O kai kuriems ko gero bus ir visai nesuprantamas;)
Dar kartą dėkui.

C
  • 1 Bir '11

Naudok set'us ir bus labai aiškus kodas:

raides = list(set(raides))

Pradžioj savo listą paverti į set'ą, set'as ištrina pasikartojančias reikšmes, paskui paverti atgal į listą... Tik verčiant į set'ą, eilės tvarka yra neišlaikoma. Jei reikia eiliškumą išlaikyti, naudok sirex pasiūlytą variantą su generatoriais.

J
  • 2 Bir '11

Dėkui.
Labai tikiuosi, kad jūsų pastangos neprapuls veltui:)