lingo 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rspec +1 -0
- data/COPYING +663 -0
- data/ChangeLog +754 -0
- data/README +322 -0
- data/Rakefile +100 -0
- data/TODO +28 -0
- data/bin/lingo +5 -0
- data/bin/lingoctl +6 -0
- data/de.lang +121 -0
- data/de/lingo-abk.txt +74 -0
- data/de/lingo-dic.txt +56822 -0
- data/de/lingo-mul.txt +3209 -0
- data/de/lingo-syn.txt +14841 -0
- data/de/test_dic.txt +24 -0
- data/de/test_mul.txt +17 -0
- data/de/test_mul2.txt +2 -0
- data/de/test_singleword.txt +2 -0
- data/de/test_syn.txt +4 -0
- data/de/test_syn2.txt +1 -0
- data/de/user-dic.txt +10 -0
- data/en.lang +113 -0
- data/en/lingo-dic.txt +55434 -0
- data/en/lingo-mul.txt +456 -0
- data/en/user-dic.txt +5 -0
- data/info/Objekte.png +0 -0
- data/info/Typen.png +0 -0
- data/info/database.png +0 -0
- data/info/db_small.png +0 -0
- data/info/download.png +0 -0
- data/info/gpl-hdr.txt +27 -0
- data/info/kerze.png +0 -0
- data/info/language.png +0 -0
- data/info/lingo.png +0 -0
- data/info/logo.png +0 -0
- data/info/meeting.png +0 -0
- data/info/types.png +0 -0
- data/lib/lingo.rb +321 -0
- data/lib/lingo/attendee/abbreviator.rb +119 -0
- data/lib/lingo/attendee/debugger.rb +111 -0
- data/lib/lingo/attendee/decomposer.rb +101 -0
- data/lib/lingo/attendee/dehyphenizer.rb +167 -0
- data/lib/lingo/attendee/multiworder.rb +301 -0
- data/lib/lingo/attendee/noneword_filter.rb +103 -0
- data/lib/lingo/attendee/objectfilter.rb +86 -0
- data/lib/lingo/attendee/sequencer.rb +190 -0
- data/lib/lingo/attendee/synonymer.rb +105 -0
- data/lib/lingo/attendee/textreader.rb +237 -0
- data/lib/lingo/attendee/textwriter.rb +196 -0
- data/lib/lingo/attendee/tokenizer.rb +218 -0
- data/lib/lingo/attendee/variator.rb +185 -0
- data/lib/lingo/attendee/vector_filter.rb +158 -0
- data/lib/lingo/attendee/wordsearcher.rb +96 -0
- data/lib/lingo/attendees.rb +289 -0
- data/lib/lingo/cli.rb +62 -0
- data/lib/lingo/config.rb +104 -0
- data/lib/lingo/const.rb +131 -0
- data/lib/lingo/ctl.rb +173 -0
- data/lib/lingo/database.rb +587 -0
- data/lib/lingo/language.rb +530 -0
- data/lib/lingo/modules.rb +98 -0
- data/lib/lingo/types.rb +285 -0
- data/lib/lingo/utilities.rb +40 -0
- data/lib/lingo/version.rb +27 -0
- data/lingo-all.cfg +85 -0
- data/lingo-call.cfg +15 -0
- data/lingo.cfg +78 -0
- data/lingo.rb +3 -0
- data/lir.cfg +72 -0
- data/porter/stem.cfg +311 -0
- data/porter/stem.rb +150 -0
- data/spec/spec_helper.rb +0 -0
- data/test.cfg +79 -0
- data/test/attendee/ts_abbreviator.rb +35 -0
- data/test/attendee/ts_decomposer.rb +31 -0
- data/test/attendee/ts_multiworder.rb +390 -0
- data/test/attendee/ts_noneword_filter.rb +19 -0
- data/test/attendee/ts_objectfilter.rb +19 -0
- data/test/attendee/ts_sequencer.rb +43 -0
- data/test/attendee/ts_synonymer.rb +33 -0
- data/test/attendee/ts_textreader.rb +58 -0
- data/test/attendee/ts_textwriter.rb +98 -0
- data/test/attendee/ts_tokenizer.rb +32 -0
- data/test/attendee/ts_variator.rb +24 -0
- data/test/attendee/ts_vector_filter.rb +62 -0
- data/test/attendee/ts_wordsearcher.rb +119 -0
- data/test/lir.csv +3 -0
- data/test/lir.txt +12 -0
- data/test/lir2.txt +12 -0
- data/test/mul.txt +1 -0
- data/test/ref/artikel.mul +1 -0
- data/test/ref/artikel.non +159 -0
- data/test/ref/artikel.seq +270 -0
- data/test/ref/artikel.syn +16 -0
- data/test/ref/artikel.vec +928 -0
- data/test/ref/artikel.ven +928 -0
- data/test/ref/artikel.ver +928 -0
- data/test/ref/lir.csv +328 -0
- data/test/ref/lir.mul +1 -0
- data/test/ref/lir.non +274 -0
- data/test/ref/lir.seq +249 -0
- data/test/ref/lir.syn +94 -0
- data/test/test_helper.rb +113 -0
- data/test/ts_database.rb +269 -0
- data/test/ts_language.rb +396 -0
- data/txt/artikel-en.txt +157 -0
- data/txt/artikel.txt +170 -0
- data/txt/lir.txt +1317 -0
- metadata +211 -0
@@ -0,0 +1,185 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# LINGO ist ein Indexierungssystem mit Grundformreduktion, Kompositumzerlegung,
|
5
|
+
# Mehrworterkennung und Relationierung.
|
6
|
+
#
|
7
|
+
# Copyright (C) 2005-2007 John Vorhauer
|
8
|
+
# Copyright (C) 2007-2011 John Vorhauer, Jens Wille
|
9
|
+
#
|
10
|
+
# This program is free software; you can redistribute it and/or modify it under
|
11
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
12
|
+
# Software Foundation; either version 3 of the License, or (at your option)
|
13
|
+
# any later version.
|
14
|
+
#
|
15
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
16
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
18
|
+
# details.
|
19
|
+
#
|
20
|
+
# You should have received a copy of the GNU Affero General Public License along
|
21
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
22
|
+
# 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
|
23
|
+
#
|
24
|
+
# For more information visit http://www.lex-lingo.de or contact me at
|
25
|
+
# welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
|
26
|
+
#
|
27
|
+
# Lex Lingo rules from here on
|
28
|
+
#++
|
29
|
+
|
30
|
+
class Lingo
|
31
|
+
|
32
|
+
# Der Variator ermöglicht bei nicht erkannten Wörtern den listenbasierten
|
33
|
+
# Austausch einzelner Wortteile einchließlich erneuter Wörterbuchsuche zur
|
34
|
+
# Verbesserung der Worterkennungsquote.
|
35
|
+
#
|
36
|
+
# Ursprünglich wurde der Variator entwickelt, um die mangelnde Qualität bei der
|
37
|
+
# OCR-Erkennung altdeutscher 's'-Konsonanten zu optimieren. Er kann ebenso bei
|
38
|
+
# alternativen Umlautschreibweisen z.B. zur Wandlung von 'Koeln' in 'Köln' dienen.
|
39
|
+
#
|
40
|
+
# === Mögliche Verlinkung
|
41
|
+
# Erwartet:: Daten vom Typ *Word* (andere werden einfach durchgereicht) z.B. von Wordsearcher
|
42
|
+
# Erzeugt:: Daten vom Typ *Word* zur Weiterleitung z.B. an Synonymer, Decomposer, Multiworder, Sequencer, Noneword_filter oder Vector_filter
|
43
|
+
#
|
44
|
+
# === Parameter
|
45
|
+
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
46
|
+
# Alle anderen Parameter müssen zwingend angegeben werden.
|
47
|
+
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
48
|
+
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
49
|
+
# <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
|
50
|
+
# <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
|
51
|
+
# <b><i>^check</i></b>:: (Standard: WA_UNKNOWN) Gebrenzt die zu variierenden Worttypen
|
52
|
+
# <b><i>marker</i></b>:: (Standard: '*') Kennzeichnung durch Variation erkannter Wörter
|
53
|
+
# <b><i>max-var</i></b>:: (Standard: '10000') Begrenzung der maximal zu prüfenden Permutationen bei der vollständigen Kombination aller auf ein Wort anzuwendenen aufgelisteten Wortteile.
|
54
|
+
#
|
55
|
+
# === Beispiele
|
56
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
57
|
+
# meeting:
|
58
|
+
# attendees:
|
59
|
+
# - textreader: { out: lines, files: '$(files)' }
|
60
|
+
# - tokenizer: { in: lines, out: token }
|
61
|
+
# - wordsearcher: { in: abbrev, out: words, source: 'sys-dic' }
|
62
|
+
# - variator: { in: words, out: varios, source: 'sys-dic' }
|
63
|
+
# - debugger: { in: varios, prompt: 'out>' }
|
64
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
65
|
+
# out> *FILE('test.txt')
|
66
|
+
# out> <*Dies = [(dies/w)]>
|
67
|
+
# out> <*ist = [(ist/t)]>
|
68
|
+
# out> <ein = [(ein/t)]>
|
69
|
+
# out> <*Tisch = [(tisch/s)]>
|
70
|
+
# out> :./PUNC:
|
71
|
+
# out> *EOL('test.txt')
|
72
|
+
# out> *EOF('test.txt')
|
73
|
+
|
74
|
+
class Attendee::Variator < Attendee
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def init
|
79
|
+
# Parameter verarbeiten
|
80
|
+
@marker = get_key('marker', '*')
|
81
|
+
@max_var = get_key('max-var', '10000').to_i
|
82
|
+
filter = get_array('check', WA_UNKNOWN)
|
83
|
+
|
84
|
+
src = get_array('source')
|
85
|
+
mod = get_key('mode', 'all')
|
86
|
+
|
87
|
+
# Daten verarbeiten
|
88
|
+
@var_strings = get_key('variations')
|
89
|
+
forward(STR_CMD_ERR, 'Ocr-variator: Konfiguration <ocr-variator> ist leer') if @var_strings.size==0
|
90
|
+
|
91
|
+
# Initialisierungen
|
92
|
+
@check = Hash.new(false)
|
93
|
+
filter.each { |s| @check[s.upcase] = true }
|
94
|
+
|
95
|
+
# Wörterbuchzugriff
|
96
|
+
@dic = Dictionary.new({'source'=>src, 'mode'=>mod}, @lingo)
|
97
|
+
@gra = Grammar.new({'source'=>src, 'mode'=>mod}, @lingo)
|
98
|
+
|
99
|
+
# Optimierungen
|
100
|
+
if @max_var == 0
|
101
|
+
forward( STR_CMD_WARN, 'Ocr-variator: max-var ist 0, setze es auf 10.000' )
|
102
|
+
@max_var = 10000
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def control(cmd, par)
|
107
|
+
# Status wird abgefragt
|
108
|
+
if cmd == STR_CMD_STATUS
|
109
|
+
# Eigenen Status um Status von Dictionary und Grammer erweitern
|
110
|
+
@dic.report.each_pair { | k, v | set( k, v ) }
|
111
|
+
@gra.report.each_pair { | k, v | set( k, v ) }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def process(obj)
|
116
|
+
# Zu prüfende Wörter filtern
|
117
|
+
if obj.is_a?(Word) && @check[obj.attr]
|
118
|
+
# Statistik für Report
|
119
|
+
inc('Anzahl gesuchter Wörter')
|
120
|
+
|
121
|
+
# Erzeuge Variationen einer Wortform
|
122
|
+
variations = [obj.form]
|
123
|
+
@var_strings.each do |switch|
|
124
|
+
from, to = switch
|
125
|
+
variations = variate(variations, from, to)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Prüfe Variation auf bekanntes Wort
|
129
|
+
variations[0...@max_var].each do |var|
|
130
|
+
# Variiertes Wort im Wörterbuch suchen
|
131
|
+
word = @dic.find_word(var)
|
132
|
+
word = @gra.find_compositum(var) if word.attr == WA_UNKNOWN
|
133
|
+
next if word.attr == WA_UNKNOWN || (
|
134
|
+
word.attr == WA_KOMPOSITUM && word.lexicals.any? { |lex|
|
135
|
+
lex.attr[0..0] == LA_TAKEITASIS
|
136
|
+
}
|
137
|
+
)
|
138
|
+
|
139
|
+
# Das erste erkannte Wort beendet die Suche
|
140
|
+
inc('Anzahl gefundener Wörter')
|
141
|
+
word.form = @marker + var
|
142
|
+
forward(word)
|
143
|
+
return
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
forward(obj)
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
# Variiere die Bestandteile eines Arrays gemäß den Austauschvorgaben.
|
153
|
+
#
|
154
|
+
# variate( 'Tiieh', 'ieh', 'sch' ) => ['Tiieh', 'Tisch']
|
155
|
+
def variate(variation_list, from, to)
|
156
|
+
# neue Varianten sammeln
|
157
|
+
add_variations = []
|
158
|
+
from_re = Regexp.new(from)
|
159
|
+
|
160
|
+
# alle Wörter in der variation_list permutieren
|
161
|
+
variation_list.each do |wordform|
|
162
|
+
|
163
|
+
# Wortform in Teile zerlegen und anschließend Dimension feststellen
|
164
|
+
wordpart = " #{wordform} ".split( from_re )
|
165
|
+
n = wordpart.size - 1
|
166
|
+
|
167
|
+
# Austauschketten in Matrix hinterlegen
|
168
|
+
change = [from, to]
|
169
|
+
|
170
|
+
# Austauschketten auf alle Teile anwenden
|
171
|
+
(1..(2**n-1)).each do |i|
|
172
|
+
variation = wordpart[0]
|
173
|
+
# i[x] = Wert des x.ten Bit von Integer i
|
174
|
+
(1..n).each { |j| variation += change[i[j-1]] + wordpart[j] }
|
175
|
+
|
176
|
+
add_variations << variation.strip
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
variation_list + add_variations
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# LINGO ist ein Indexierungssystem mit Grundformreduktion, Kompositumzerlegung,
|
5
|
+
# Mehrworterkennung und Relationierung.
|
6
|
+
#
|
7
|
+
# Copyright (C) 2005-2007 John Vorhauer
|
8
|
+
# Copyright (C) 2007-2011 John Vorhauer, Jens Wille
|
9
|
+
#
|
10
|
+
# This program is free software; you can redistribute it and/or modify it under
|
11
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
12
|
+
# Software Foundation; either version 3 of the License, or (at your option)
|
13
|
+
# any later version.
|
14
|
+
#
|
15
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
16
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
18
|
+
# details.
|
19
|
+
#
|
20
|
+
# You should have received a copy of the GNU Affero General Public License along
|
21
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
22
|
+
# 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
|
23
|
+
#
|
24
|
+
# For more information visit http://www.lex-lingo.de or contact me at
|
25
|
+
# welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
|
26
|
+
#
|
27
|
+
# Lex Lingo rules from here on
|
28
|
+
#++
|
29
|
+
|
30
|
+
class Lingo
|
31
|
+
|
32
|
+
# Die Hauptaufgabe des Vector_filter ist die Erstellung eines Dokumenten-Index-Vektor.
|
33
|
+
# Dabei werden die durch die anderen Attendees ermittelten Grundformen eines Wortes
|
34
|
+
# gespeichert und bei einem Datei- oder Record-Wechsel weitergeleitet. Der Vector_filter
|
35
|
+
# kann bestimmte Wortklassen filtern und die Ergebnisse in verschiedenen Arten aufbereiten.
|
36
|
+
# Dabei werden Funktionen wie das einfache Zählen der Häufigkeit innerhalb eines Dokuments,
|
37
|
+
# aber auch die Term-Frequenz und unterschiedliche Ausgabeformate unterstützt.
|
38
|
+
#
|
39
|
+
# === Mögliche Verlinkung
|
40
|
+
# Erwartet:: Daten vom Typ *Word*, z.B. von Abbreviator, Wordsearcher, Decomposer, Synonymer, Multiworder, Sequencer
|
41
|
+
# Erzeugt:: Daten vom Typ *String*, z.B. für Textwriter
|
42
|
+
#
|
43
|
+
# === Parameter
|
44
|
+
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
45
|
+
# Alle anderen Parameter müssen zwingend angegeben werden.
|
46
|
+
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
47
|
+
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
48
|
+
# <b><i>lexicals</i></b>:: (Standard: '[sy]' => die Wortklassen Substantiv und Synonyme werden gefiltert)
|
49
|
+
# Es können in eckige Klammern beliebige Wortklassen angegeben werden (siehe lib/strings.rb).
|
50
|
+
# Der Parameter wird als regulärer Ausdruck ausgewertet.
|
51
|
+
# <b><i>sort</i></b>:: (Standard: 'normal')
|
52
|
+
# Der Parameter +sort+ beeinflußt Verarbeitung und Ausgabeformat des Vector_filters.
|
53
|
+
# normal:: Jedes gefilterte Wort wird einmalig (keine Doppelnennungen!) in
|
54
|
+
# alphabetischer Reihenfolge in der Form "wort" ausgegeben.
|
55
|
+
# term_abs:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
|
56
|
+
# der absoluten Häufigkeit im Dokument in der Form "12 wort" ausgegeben.
|
57
|
+
# term_rel:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
|
58
|
+
# der relativen Häufigkeit im Dokument in der Form "0.1234 wort" ausgegeben.
|
59
|
+
# sto_abs:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
|
60
|
+
# der absoluten Häufigkeit im Dokument in der Form "wort {12}" ausgegeben.
|
61
|
+
# sto_rel:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
|
62
|
+
# der relativen Häufigkeit im Dokument in der Form "wort {0.1234}" ausgegeben.
|
63
|
+
# <b><i>skip</i></b>:: (Standard: TA_PUNCTUATION und TA_OTHER) Hiermit wird angegeben, welche Objekte nicht
|
64
|
+
# verarbeitet werden sollen. Die +skip+-Angabe bezieht sich auf das Attribut +attr+ von
|
65
|
+
# Token oder Word-Objekten.
|
66
|
+
#
|
67
|
+
# === Beispiele
|
68
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
69
|
+
# meeting:
|
70
|
+
# attendees:
|
71
|
+
# - textreader: { out: lines, files: '$(files)' }
|
72
|
+
# - tokenizer: { in: lines, out: token }
|
73
|
+
# - wordsearcher: { in: token, out: words, source: 'sys-dic' }
|
74
|
+
# - vector_filter: { in: words, out: filtr, sort: 'term_rel' }
|
75
|
+
# - debugger: { in: filtr, prompt: 'out>' }
|
76
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
77
|
+
# out> *FILE('test.txt')
|
78
|
+
# out> "0.28571 indexierung"
|
79
|
+
# out> *EOF('test.txt')
|
80
|
+
|
81
|
+
class Attendee::Vector_filter < Attendee
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
def init
|
86
|
+
@lexis = Regexp.new(get_key('lexicals', '[sy]').downcase)
|
87
|
+
@sort = get_key('sort', 'normal').downcase
|
88
|
+
@skip = get_array('skip', TA_PUNCTUATION+','+TA_OTHER).collect {|s| s.upcase }
|
89
|
+
@vectors = Array.new
|
90
|
+
@word_count = 0
|
91
|
+
|
92
|
+
if @debug = get_key('debug', false)
|
93
|
+
@prompt = get_key('prompt', 'lex:) ')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def control(cmd, par)
|
98
|
+
case cmd
|
99
|
+
when STR_CMD_EOL
|
100
|
+
deleteCmd
|
101
|
+
when STR_CMD_FILE, STR_CMD_RECORD, STR_CMD_EOF
|
102
|
+
@debug ? @vectors.each { |str| forward(str) } : sendVector
|
103
|
+
@vectors.clear
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def process(obj)
|
108
|
+
if @debug
|
109
|
+
@vectors << "#{@prompt} #{obj.inspect}" if eval(@debug)
|
110
|
+
elsif obj.is_a?(Word)
|
111
|
+
@word_count += 1 if @skip.index(obj.attr).nil?
|
112
|
+
unless obj.lexicals.nil?
|
113
|
+
lexis = obj.get_class(@lexis) #lexicals.collect { |lex| (lex.attr =~ @lexis) ? lex : nil }.compact # get_class(@lexis)
|
114
|
+
lexis.each { |lex| @vectors << lex.form.downcase }
|
115
|
+
add('Anzahl von Vektor-Wörtern', lexis.size)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def sendVector
|
123
|
+
return if @vectors.size==0
|
124
|
+
|
125
|
+
add('Objekte gefiltert', @vectors.size)
|
126
|
+
|
127
|
+
# Array der Vector-Wörter zählen und nach Häufigkeit sortieren
|
128
|
+
if @sort=='normal'
|
129
|
+
@vectors = @vectors.compact.sort.uniq
|
130
|
+
else
|
131
|
+
cnt = Hash.new(0)
|
132
|
+
@vectors.compact.each { |e| cnt[e]+=1 }
|
133
|
+
@vectors = cnt.to_a.sort { |x,y|
|
134
|
+
if (y[1]<=>x[1])==0
|
135
|
+
x[0]<=>y[0]
|
136
|
+
else
|
137
|
+
y[1]<=>x[1]
|
138
|
+
end
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
# Vectoren je nach Parameter formatiert weiterleiten
|
143
|
+
@vectors.collect { |vec|
|
144
|
+
case @sort
|
145
|
+
when 'term_abs' then sprintf "%d %s", vec[1], vec[0]
|
146
|
+
when 'term_rel' then sprintf "%6.5f %s", vec[1].to_f/@word_count, vec[0]
|
147
|
+
when 'sto_abs' then sprintf "%s {%d}", vec[0], vec[1]
|
148
|
+
when 'sto_rel' then sprintf "%s {%6.5f}", vec[0], vec[1].to_f/@word_count
|
149
|
+
else sprintf "%s", vec
|
150
|
+
end
|
151
|
+
}.each { |str| forward(str) }
|
152
|
+
|
153
|
+
@word_count = 0 if @sort == 'sto_rel'
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# LINGO ist ein Indexierungssystem mit Grundformreduktion, Kompositumzerlegung,
|
5
|
+
# Mehrworterkennung und Relationierung.
|
6
|
+
#
|
7
|
+
# Copyright (C) 2005-2007 John Vorhauer
|
8
|
+
# Copyright (C) 2007-2011 John Vorhauer, Jens Wille
|
9
|
+
#
|
10
|
+
# This program is free software; you can redistribute it and/or modify it under
|
11
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
12
|
+
# Software Foundation; either version 3 of the License, or (at your option)
|
13
|
+
# any later version.
|
14
|
+
#
|
15
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
16
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
18
|
+
# details.
|
19
|
+
#
|
20
|
+
# You should have received a copy of the GNU Affero General Public License along
|
21
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
22
|
+
# 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
|
23
|
+
#
|
24
|
+
# For more information visit http://www.lex-lingo.de or contact me at
|
25
|
+
# welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
|
26
|
+
#
|
27
|
+
# Lex Lingo rules from here on
|
28
|
+
#++
|
29
|
+
|
30
|
+
class Lingo
|
31
|
+
|
32
|
+
# Der Wordsearcher ist das Herzstück von Lingo. Er macht die Hauptarbeit und versucht
|
33
|
+
# alle Token die nach einem sinnvollen Wort aussehen, in den ihm angegebenen
|
34
|
+
# Wörterbüchern zu finden und aufzulösen. Dabei werden die im Wörterbuch gefundenen
|
35
|
+
# Grundformen inkl. Wortklassen an das Word-Objekt angehängt.
|
36
|
+
#
|
37
|
+
# === Mögliche Verlinkung
|
38
|
+
# Erwartet:: Daten vom Typ *Token* (andere werden einfach durchgereicht) z.B. von Tokenizer, Abbreviator
|
39
|
+
# Erzeugt:: Daten vom Typ *Word* für erkannte Wörter z.B. für Synonymer, Decomposer, Ocr_variator, Multiworder, Sequencer, Noneword_filter, Vector_filter
|
40
|
+
#
|
41
|
+
# === Parameter
|
42
|
+
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
43
|
+
# Alle anderen Parameter müssen zwingend angegeben werden.
|
44
|
+
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
45
|
+
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
46
|
+
# <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
|
47
|
+
# <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
|
48
|
+
#
|
49
|
+
# === Beispiele
|
50
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
51
|
+
# meeting:
|
52
|
+
# attendees:
|
53
|
+
# - textreader: { out: lines, files: '$(files)' }
|
54
|
+
# - tokenizer: { in: lines, out: token }
|
55
|
+
# - abbreviator: { in: token, out: abbrev, source: 'sys-abk' }
|
56
|
+
# - wordsearcher: { in: abbrev, out: words, source: 'sys-dic' }
|
57
|
+
# - debugger: { in: words, prompt: 'out>' }
|
58
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
59
|
+
# out> *FILE('test.txt')
|
60
|
+
# out> <Dies = [(dies/w)]>
|
61
|
+
# out> <ist = [(sein/v)]>
|
62
|
+
# out> <ggf. = [(gegebenenfalls/w)]>
|
63
|
+
# out> <eine = [(einen/v), (ein/w)]>
|
64
|
+
# out> <Abk³rzung = [(abk³rzung/s)]>
|
65
|
+
# out> :./PUNC:
|
66
|
+
# out> *EOL('test.txt')
|
67
|
+
# out> *EOF('test.txt')
|
68
|
+
|
69
|
+
class Attendee::Wordsearcher < Attendee
|
70
|
+
|
71
|
+
def init
|
72
|
+
# Wörterbuch bereitstellen
|
73
|
+
src = get_array('source')
|
74
|
+
mod = get_key('mode', 'all')
|
75
|
+
@dic = Dictionary.new({'source'=>src, 'mode'=>mod}, @lingo)
|
76
|
+
end
|
77
|
+
|
78
|
+
def control(cmd, par)
|
79
|
+
@dic.report.each_pair { |key, value|
|
80
|
+
set(key, value)
|
81
|
+
} if cmd == STR_CMD_STATUS
|
82
|
+
end
|
83
|
+
|
84
|
+
def process(obj)
|
85
|
+
if obj.is_a?(Token) && obj.attr == TA_WORD
|
86
|
+
inc('Anzahl gesuchter Wörter')
|
87
|
+
word = @dic.find_word(obj.form)
|
88
|
+
inc('Anzahl gefundener Wörter') unless word.attr == WA_UNKNOWN
|
89
|
+
obj = word
|
90
|
+
end
|
91
|
+
forward(obj)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,289 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# LINGO ist ein Indexierungssystem mit Grundformreduktion, Kompositumzerlegung,
|
5
|
+
# Mehrworterkennung und Relationierung.
|
6
|
+
#
|
7
|
+
# Copyright (C) 2005-2007 John Vorhauer
|
8
|
+
# Copyright (C) 2007-2011 John Vorhauer, Jens Wille
|
9
|
+
#
|
10
|
+
# This program is free software; you can redistribute it and/or modify it under
|
11
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
12
|
+
# Software Foundation; either version 3 of the License, or (at your option)
|
13
|
+
# any later version.
|
14
|
+
#
|
15
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
16
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
18
|
+
# details.
|
19
|
+
#
|
20
|
+
# You should have received a copy of the GNU Affero General Public License along
|
21
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
22
|
+
# 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
|
23
|
+
#
|
24
|
+
# For more information visit http://www.lex-lingo.de or contact me at
|
25
|
+
# welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
|
26
|
+
#
|
27
|
+
# Lex Lingo rules from here on
|
28
|
+
#++
|
29
|
+
|
30
|
+
require_relative 'modules'
|
31
|
+
require_relative 'language'
|
32
|
+
require_relative 'const'
|
33
|
+
require_relative 'types'
|
34
|
+
|
35
|
+
class Lingo
|
36
|
+
|
37
|
+
# Lingo ist als universelles Indexierungssystem entworfen worden. Seine Stärke liegt in der einfachen Konfigurierbarkeit für
|
38
|
+
# spezifische Aufgaben und in der schnelle Entwicklung weiterer Funktionen durch systematischen Kapselung der Komplexität auf
|
39
|
+
# kleine Verarbeitungseinheiten. Die kleinste Verarbeitungseinheit wird Attendee genannt. Um ein gewünschtes Verarbeitungsergebnis
|
40
|
+
# zu bekommen, werden die benötigten Attendees einfach in einer Reihe hinter einander geschaltet. Ein einfaches Beispiel hierfür ist
|
41
|
+
# eine direkte Verbindung zwischen einem Textreader, einem Tokenizer und einem Textwriter. Alle drei Klassen sind von der Klasse
|
42
|
+
# Attendee abgeleitet.
|
43
|
+
#
|
44
|
+
# Der Textreader liest beispielsweise Zeilen aus einer Textdatei und leitet sie weiter an den Tokenizer. Der Tokenizer zerlegt eine
|
45
|
+
# Textzeile in einzelne Wörter und gibt diese weiter an den Textwriter, der diese in eine (andere) Datei schreibt. Über vielfältige
|
46
|
+
# Konfigurationsmöglichkeiten kann das Verhalten der Attendees an die eigenen Bedürfnisse angepasst werden.
|
47
|
+
#
|
48
|
+
# Die Verkettung einzelner Attendees findet über die Schnittstellen +listen+ und +talk+ statt. An +listen+ können beliebige Objekte
|
49
|
+
# zur Ver- und Bearbeitung übergeben werden. Nach der Verarbeitung werden sie mittels +talk+ an die verketteten Attendees weiter
|
50
|
+
# gegeben. Objekte der Klasse AgendaItem dienen dabei der Steuerung der Verarbeitung und sind nicht Bestandteil der normalen
|
51
|
+
# Verarbeitung. Beispiele für AgendaItems sind die Kommandos TALK (Aufforderung zum Start der Verarbeitung), WARN (zur Ausgabe von
|
52
|
+
# Warnungen eines Attendees) und EOL (End of Line, Ende einer Textzeile nach Zerlegung in einzelne Wörter). Eine vollständige
|
53
|
+
# Übersicht benutzer AgendaItems (oder auf Stream Commands) steht in lib/const.rb mit dem Prefix STR_CMD_.
|
54
|
+
#
|
55
|
+
# Um die Entwicklung von neuen Attendees zu beschleunigen, wird durch die Vererbung sind bei wird die gesammte sind in der Regel nur
|
56
|
+
# drei abstrakte Methoden zu implementieren: +init+, +control+ und +process+. Die Methode +init+ wird bei der Instanziierung eines
|
57
|
+
# Objektes einmalig aufgerufen. Sie dient der Vorbereitung der Verarbeitung, z.B. durch das Öffnen und Bereitstellen von
|
58
|
+
# Wörterbüchern zur linguistischen Analyse. An die Methode +control+ werden alle eingehenden AgendaItems weitergeleitet. Dort erfolgt
|
59
|
+
# die Verarbeitungssteuerung, also z.B. bei STR_CMD_FILE das Öffnen einer Datei und bei STR_CMD_EOF respektive das Schließen. Die
|
60
|
+
# echte Verarbeitung von Daten findet daher durch die Methode +process+ statt.
|
61
|
+
#
|
62
|
+
# was macht attendee
|
63
|
+
# - verkettung der attendees anhand von konfigurationsinformationen
|
64
|
+
# - bereitstellung von globalen und spezifischen konfigurationsinformationen
|
65
|
+
# - behandlung von bestimmten übergreifenden Kommandos, z.B. STR_CMD_TALK, STR_CMD_STATUS, STR_CMD_WARN, STR_CMD_ERR
|
66
|
+
# - separierung und routing von kommando bzw. datenobjekten
|
67
|
+
#
|
68
|
+
# was macht die abgeleitet klasse
|
69
|
+
# - verarbeitet und/oder transformiert datenobjekte
|
70
|
+
# - wird gesteuert durch kommandos
|
71
|
+
# - schreibt verarbeitungsstatistiken
|
72
|
+
|
73
|
+
class Attendee
|
74
|
+
|
75
|
+
include Reportable
|
76
|
+
|
77
|
+
def initialize(config, lingo)
|
78
|
+
@lingo = lingo
|
79
|
+
|
80
|
+
init_reportable
|
81
|
+
|
82
|
+
begin
|
83
|
+
lingo.dictionary_config
|
84
|
+
rescue
|
85
|
+
raise "Fehler in der .lang-Datei bei 'language/dictionary'"
|
86
|
+
end
|
87
|
+
|
88
|
+
@config, @subscriber = config, []
|
89
|
+
|
90
|
+
init if self.class.method_defined?(:init)
|
91
|
+
|
92
|
+
@attendee_can_control = self.class.method_defined?(:control)
|
93
|
+
@attendee_can_process = self.class.method_defined?(:process)
|
94
|
+
|
95
|
+
@skip_this_command, @start_of_processing = false, nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def add_subscriber(subscriber)
|
99
|
+
@subscriber.concat(subscriber)
|
100
|
+
end
|
101
|
+
|
102
|
+
def listen(obj)
|
103
|
+
unless obj.is_a?(AgendaItem)
|
104
|
+
if @attendee_can_process
|
105
|
+
inc(STA_NUM_OBJECTS)
|
106
|
+
|
107
|
+
unless @lingo.report_time
|
108
|
+
process(obj)
|
109
|
+
else
|
110
|
+
@start_of_processing = Time.new
|
111
|
+
process(obj)
|
112
|
+
add(STA_TIM_OBJECTS, Time.new - @start_of_processing)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
forward(obj)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
# Neuen TOP verarbeiten
|
119
|
+
if @attendee_can_control
|
120
|
+
inc(STA_NUM_COMMANDS)
|
121
|
+
|
122
|
+
unless @lingo.report_time
|
123
|
+
control(obj.cmd, obj.param)
|
124
|
+
else
|
125
|
+
@start_of_processing = Time.new
|
126
|
+
control(obj.cmd, obj.param)
|
127
|
+
add(STA_TIM_COMMANDS, Time.new - @start_of_processing)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Spezialbehandlung für einige TOPs nach Verarbeitung
|
132
|
+
case obj.cmd
|
133
|
+
when STR_CMD_TALK
|
134
|
+
# keine weitere Behandlung oder Weiterleitung
|
135
|
+
nil
|
136
|
+
when STR_CMD_STATUS
|
137
|
+
# Standardprotokollinformationen ausgeben
|
138
|
+
|
139
|
+
if @lingo.report_time
|
140
|
+
@lingo.config.stderr.puts 'Perf: %-15s => %7d commands in %s (%s/cmd), %8d objects in %s (%s/obj)' % [
|
141
|
+
@config['name'],
|
142
|
+
get(STA_NUM_COMMANDS),
|
143
|
+
seconds_to_str(get(STA_TIM_COMMANDS)),
|
144
|
+
seconds_to_str((get(STA_NUM_COMMANDS)==0) ? 0.0 : get(STA_TIM_COMMANDS) / get(STA_NUM_COMMANDS).to_f),
|
145
|
+
get(STA_NUM_OBJECTS),
|
146
|
+
seconds_to_str(get(STA_TIM_OBJECTS)),
|
147
|
+
seconds_to_str((get(STA_NUM_OBJECTS)==0) ? 0.0 : get(STA_TIM_OBJECTS) / get(STA_NUM_OBJECTS).to_f)
|
148
|
+
]
|
149
|
+
end
|
150
|
+
|
151
|
+
if @lingo.report_status
|
152
|
+
@lingo.config.stderr.puts "Attendee <%s> was connected from '%s' to '%s' reporting...\n" % @config.values_at(*%w[name in out]),
|
153
|
+
report.sort.map { |info| " #{info[0]} = #{info[1]}" }, nil
|
154
|
+
end
|
155
|
+
|
156
|
+
forward(obj.cmd, obj.param)
|
157
|
+
else
|
158
|
+
if @skip_this_command
|
159
|
+
@skip_this_command = false
|
160
|
+
else
|
161
|
+
forward(obj.cmd, obj.param)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def talk(obj)
|
168
|
+
time_for_sub = Time.new if @lingo.report_time
|
169
|
+
@subscriber.each { |attendee| attendee.listen(obj) }
|
170
|
+
@start_of_processing += (Time.new - time_for_sub) if @lingo.report_time
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
# ---------------------------------------------------
|
176
|
+
# Create intelligent output of performance times
|
177
|
+
# measured with command line option -p
|
178
|
+
# ---------------------------------------------------
|
179
|
+
|
180
|
+
def seconds_to_str(float)
|
181
|
+
if float < 0.001
|
182
|
+
"%9.3f µs" % [float * 1000000.0]
|
183
|
+
elsif float < 1.0
|
184
|
+
"%9.3f ms" % [float * 1000.0]
|
185
|
+
elsif float < 60.0
|
186
|
+
"%9.3f s " % [float]
|
187
|
+
elsif float < 3600.0
|
188
|
+
"%9.3f m " % [float / 60.0]
|
189
|
+
else
|
190
|
+
"%9.3f h " % [float / 3600.0]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def deleteCmd
|
195
|
+
@skip_this_command = true
|
196
|
+
end
|
197
|
+
|
198
|
+
def forward(obj, param=nil)
|
199
|
+
if param.nil?
|
200
|
+
# Information weiterreichen
|
201
|
+
talk(obj)
|
202
|
+
else
|
203
|
+
# TOP weiterreichen (wenn keine Warnung oder Fehler)
|
204
|
+
case obj
|
205
|
+
when STR_CMD_WARN then printf "+%s\n| %s: %s\n+%s\n", '-'*60, @config['name'], param, '-'*60
|
206
|
+
when STR_CMD_ERR then printf "%s\n= %s: %s\n%s\n", '='*61, @config['name'], param, '='*61; exit( 1 )
|
207
|
+
else
|
208
|
+
talk(AgendaItem.new(obj, param))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# ---------------------------------------------------
|
214
|
+
# Konfigurationshilfsmethoden
|
215
|
+
# ---------------------------------------------------
|
216
|
+
def has_key?(key)
|
217
|
+
!@config.nil? && @config.has_key?(key)
|
218
|
+
end
|
219
|
+
|
220
|
+
def get_key(key, default=nil)
|
221
|
+
forward(STR_CMD_ERR, "Attribut #{key} nicht gesetzt") if default.nil? && !has_key?(key)
|
222
|
+
@config.fetch(key, default)
|
223
|
+
end
|
224
|
+
|
225
|
+
def get_array(key, default=nil)
|
226
|
+
get_key(key, default).split(STRING_SEPERATOR_PATTERN)
|
227
|
+
end
|
228
|
+
|
229
|
+
# ---------------------------------------------------
|
230
|
+
# Abstrakte Methoden
|
231
|
+
#
|
232
|
+
# init
|
233
|
+
# control(cmd, param)
|
234
|
+
# process(obj)
|
235
|
+
# ---------------------------------------------------
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
class BufferedAttendee < Attendee
|
240
|
+
|
241
|
+
BufferInsert = Struct.new(:position, :object)
|
242
|
+
|
243
|
+
def initialize(config, lingo)
|
244
|
+
# In den Buffer werden alle Objekte geschrieben, bis process_buffer? == true ist
|
245
|
+
@buffer = []
|
246
|
+
|
247
|
+
# deferred_inserts beeinflussen nicht die Buffer-Größe, sondern werden an einer
|
248
|
+
# bestimmten Stelle in den Datenstrom eingefügt
|
249
|
+
@deferred_inserts = []
|
250
|
+
|
251
|
+
super
|
252
|
+
end
|
253
|
+
|
254
|
+
protected
|
255
|
+
|
256
|
+
def process(obj)
|
257
|
+
@buffer.push(obj)
|
258
|
+
process_buffer if process_buffer?
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
|
263
|
+
def forward_buffer
|
264
|
+
# Aufgeschobene Einfügungen in Buffer kopieren
|
265
|
+
@deferred_inserts.sort_by { |ins| ins.position }.each { |ins|
|
266
|
+
@buffer.insert(ins.position, ins.object)
|
267
|
+
}
|
268
|
+
@deferred_inserts.clear
|
269
|
+
|
270
|
+
# Buffer weiterleiten
|
271
|
+
@buffer.each { |obj| forward(obj) }
|
272
|
+
@buffer.clear
|
273
|
+
end
|
274
|
+
|
275
|
+
def process_buffer?
|
276
|
+
true
|
277
|
+
end
|
278
|
+
|
279
|
+
def process_buffer
|
280
|
+
# to be defined by child class
|
281
|
+
end
|
282
|
+
|
283
|
+
def deferred_insert(pos, obj)
|
284
|
+
@deferred_inserts << BufferInsert.new(pos, obj)
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|