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.