Kirjoittaja: Chiman
Kirjoitettu: 12.12.2012 – 12.12.2012
Tagit: algoritmit, teksti, koodi näytille, vinkki
Ohjelma hakee sanalistasta annettuun konsonantti-vokaali-sääntöön sopivat sanat. Käyttökohteena on esimerkiksi krypto-ristikoiden sanojen etsiminen, jolloin sanalistana voidaan käyttää esim. putkapostin tehtävänannosta löytyvää suomen kielen sanastoa.
Ohjelma käyttää työhakemiston sanalista.txt-tiedostoa lähteenä, jos sellainen on.
Ohjelma on tehty toimimaan 2- ja 3-sarjan Python-versioilla.
#!/usr/bin/env python # -*- coding: utf-8 -*- # sanaosumat.py import sys def _ehto_samat_merkit(i, j): return lambda sana: sana[i] == sana[j] def _ehto_eri_merkit(i, j): return lambda sana: sana[i] != sana[j] def _ehto_merkki_joukossa(i, joukko): return lambda sana: sana[i] in joukko def _rakenna_sanaehdot(merkit): if sys.version < '3': vokaalit = unicode('aeiouyåäö', 'utf-8') konsot = unicode('bcdfghjklmnpqrstvwxz', 'utf-8') else: vokaalit = 'aeiouyåäö' konsot = 'bcdfghjklmnpqrstvwxz' # Tehdään sanapituuden täsmäystarkistus aluksi, jotta indeksitarkistukset # eivät voi aiheuttaa virheitä. ehdot = [lambda sana: len(sana) == len(merkit)] for i, m in enumerate(merkit): eka_sijainti = merkit.index(m) if len(m) > 1 and i != eka_sijainti: # Aiempi vastaava on jo ollut, tarkistukseksi vain identtisyys siihen. ehdot.append(_ehto_samat_merkit(i, eka_sijainti)) continue if len(m) > 1: # Ensimmäinen tällainen yhdistelmä. Tee erisuuruustarkistus edeltäviin # saman merkkiryhmän eri numeroihin, esim. v1 != v2. merkitty = set() for j, n in enumerate(merkit[:i]): if len(n) > 1 and n[0] == m[0] and n != m and n not in merkitty: ehdot.append(_ehto_eri_merkit(i, j)) merkitty.add(n) if m[0] == 'v': ehdot.append(_ehto_merkki_joukossa(i, vokaalit)) elif m[0] == 'k': ehdot.append(_ehto_merkki_joukossa(i, konsot)) return ehdot def osuvat(lista, s): """iteroi listan sanat, jotka täyttävät argumentin s muodon Muoto koostuu kirjaimista kvx ja numeroista 0-9. k = konsonantti, v = vokaali, x = vokaali tai konsonantti Pelkkä k, v tai x ei sido kirjainta sanan muihin kirjaimiin. Kaikki muodon keskenään identtiset kirjain-numero-yhdistelmät edustavat keskenään samaa kirjainta. Vastaavasti eri kirjain-numero-yhdistelmät edustavat keskenään eri kirjaimia. Muodon välilyönneillä ei ole merkitystä. Esimerkit: 'v1 x v1' sopii sanoihin 'aha' ja 'aaa' muttei sanaan 'asu' 'v1 x v2' sopii sanoihin 'ajo' ja 'aie' muttei sanaan 'aha' 'k1vk2v' sopii sanoihin 'kala' ja 'siru' muttei sanaan 'sisu' 'x1vvx2' sopii sanoihin 'taas' ja 'säie' muttei sanaan 'noin' """ import re # sallitaan vain oikeista yhdistelmistä koostuva muoto if not re.match(r'^( *[kvx][0-9]*)+ *$', s): raise ValueError('Virheellinen muoto') merkit = re.findall(r'([kvx][0-9]*)', s) ehdot = _rakenna_sanaehdot(merkit) for sana in lista: if all(ehto(sana) for ehto in ehdot): yield sana def _hae_sanalista(): try: if sys.version < '3': sanat = open('sanalista.txt').readlines() sanat = map(lambda x: x.strip().decode('latin1'), sanat) else: sanat = open('sanalista.txt', encoding='latin1').readlines() sanat = map(lambda x: x.strip(), sanat) # jos sanalista on utf-8 -muodossa, muuta se ylle except: if sys.version < '3': sanat = map(unicode, ['osu', 'aha', 'kari', 'kivi']) else: sanat = ['osu', 'aha', 'kari', 'kivi'] return sanat if __name__ == '__main__': if len(sys.argv) > 1: muoto = ''.join(sys.argv[1:]) sanat = _hae_sanalista() tulos = ', '.join(osuvat(sanat, muoto)) if tulos: if sys.version < '3': print(tulos.encode('utf-8')) else: print(tulos) else: muoto_ohje = osuvat.__doc__.split('\n', 1)[1] print('Käyttö: python %s MUOTO\n%s' % (sys.argv[0], muoto_ohje))
Käyttö komentoriviltä:
python sanaosumat.py k1 v k1 v k2
tulostaa:
bebop, kukin, sisal, sisar, sisin, tatar, tutor
kun käytössä on Ohjelmointiputkan versio kotus-sanalistasta.
Lisäys: Koodi muokattu pyynnöstä Python 3 -yhteensopivaksi.