2 # -*- coding: utf-8 -*-
3 # Copyright © 2010,2011 Fundacja Nowoczesna Polska
5 # This file is part of Leśmianator.
7 # Leśmianator is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # Leśmianator is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with Leśmianator. If not, see <http://www.gnu.org/licenses/>.
22 Leśmianator - program generujący wiersze na życzenie.
24 Wiersz generowany jest według następującego algorytmu:
25 Każdy kolejny znak jest losowany zgodnie z wyznaczoną wcześniej częstością
26 występowania znaków w tekstach źródłowych w kontekście trzech poprzednich
29 Przykładowo, jeśli dotąd wygenerowaliśmy ciąg "Litw", to bierzemy pod uwagę
30 ciąg "itw". Jeśli w plikach źródłowych np. dwa razy pojawił się ciąg "itwo"
31 i raz ciąg "itwa", to kolejny znak losujemy między 'o' (z prawdopodobieństwem
32 2/3) a 'a' (z prawdopodobieństwem 1/3).
34 Wszystkie litery w plikach źródłowych są najpierw zamieniane na małe.
35 Białe znaki traktowane są tak samo, jak wszystkie pozostałe.
37 Leśmianator kończy pracę wraz z ukończeniem strofy (tj. wstawieniem pustej
38 linii), o ile napisał co najmniej dwa niepuste wersy (w przeciwnych przypadku
39 zaczyna kolejną strofę).
43 from collections import Counter, defaultdict
44 from os.path import abspath, dirname, join
45 import cPickle as pickle
46 from random import randint
50 class Lesmianator(object):
56 DATA_FILE = join(dirname(abspath(__file__)), 'data.p')
60 self.continuations = defaultdict(Counter)
63 """Ładuje wyniki analizy z pliku."""
64 with open(self.DATA_FILE) as f:
65 self.continuations = pickle.load(f)
68 """Zapisuje wyniki analizy do pliku."""
69 with open(self.DATA_FILE, 'w') as f:
70 pickle.dump(self.continuations, f)
72 def add_text(self, text):
73 """Wykonuje właściwą analizę tekstu źródłowego.
75 Zamienia tekst na małe litery i dla każdego znaku zapisuje, po jakim
76 ciągu trzech znaków wystąpił.
80 text = unicode(text, 'utf-8').lower()
82 self.continuations[last_word][letter] += 1
83 last_word = last_word[-self.SAMPLE_LENGTH + 1:] + letter
85 re_txt_file = re.compile(r'\n{3,}(.*?)\n*-----\n', re.S).search
86 def add_txt_file(self, txt_file):
87 """Dodaje plik tekstowy do analizy.
89 Pliki tekstowe z Wolnych Lektur zawierają na początku nazwisko
90 autora, tytuł i podtytuł utworu, następnie kilka pustych linii,
91 tekst utworu i oddzieloną kreskami stopkę. Funkcja wyciana z tego
92 sam goły tekst i przekazuje go do właściwej analizy statystycznej.
95 m = self.re_txt_file(txt_file.read())
96 self.add_text(m.group(1))
98 def choose_letter(self, word):
99 """Losuje kolejny znak wiersza."""
100 if word not in self.continuations:
103 choices = sum((self.continuations[word][letter]
104 for letter in self.continuations[word]))
105 r = randint(0, choices - 1)
107 for letter in self.continuations[word]:
108 r -= self.continuations[word][letter]
113 """Zwraca wygenerowany wiersz."""
117 finished_stanza_verses = 0
118 current_stanza_verses = 0
123 # kończy pracę, jeśli ukończone strofy zawierają co najmniej
124 # dwie niepuste linie
125 while finished_stanza_verses < self.MIN_LINES and char_count < self.MAX_LEN:
126 letter = self.choose_letter(word)
127 letters.append(letter)
128 word = word[-self.SAMPLE_LENGTH + 1:] + letter
133 finished_stanza_verses += current_stanza_verses
134 current_stanza_verses = 0
136 current_stanza_verses += 1
141 return ''.join(letters).strip()
144 if __name__ == '__main__':