lingo 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|