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,530 @@
|
|
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 'const'
|
31
|
+
require_relative 'modules'
|
32
|
+
require_relative 'database'
|
33
|
+
|
34
|
+
class Lingo
|
35
|
+
|
36
|
+
# Die Klasse LexicalHash ermöglicht den Zugriff auf die Lingodatenbanken. Im Gegensatz zur
|
37
|
+
# Klasse DbmFile, welche nur Strings als Ergebnis zurück gibt, wird hier als Ergebnis ein
|
38
|
+
# Array von Lexical-Objekten zurück gegeben.
|
39
|
+
|
40
|
+
class LexicalHash
|
41
|
+
|
42
|
+
include Cachable
|
43
|
+
include Reportable
|
44
|
+
|
45
|
+
def initialize(id, lingo)
|
46
|
+
init_reportable
|
47
|
+
init_cachable
|
48
|
+
report_prefix( id )
|
49
|
+
|
50
|
+
# Parameter aus de.lang:language/dictionary/databases auslesen
|
51
|
+
config = lingo.config['language/dictionary/databases/' + id]
|
52
|
+
Lingo.error( "LexicalHash kann Datenquelle mit ID '#{id}' in de.lang:language/dictionary/databases' nicht finden" ) if config.nil?
|
53
|
+
|
54
|
+
@wordclass = config.fetch( 'def-wc', LA_UNKNOWN )
|
55
|
+
|
56
|
+
# Store erzeugen
|
57
|
+
@source = DbmFile.new(id, lingo)
|
58
|
+
@source.open
|
59
|
+
end
|
60
|
+
|
61
|
+
def close
|
62
|
+
@source.close
|
63
|
+
end
|
64
|
+
|
65
|
+
def [](ikey)
|
66
|
+
# Schlüssel normalisieren
|
67
|
+
inc('total requests')
|
68
|
+
key = ikey.downcase
|
69
|
+
|
70
|
+
# Cache abfragen
|
71
|
+
if hit?(key)
|
72
|
+
inc('cache hits')
|
73
|
+
return retrieve(key)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Wert aus Datenbank lesen
|
77
|
+
inc('source reads')
|
78
|
+
record = @source[key]
|
79
|
+
|
80
|
+
# Werte in interne Objekte umwandeln
|
81
|
+
record = record.collect do |str|
|
82
|
+
case str
|
83
|
+
when /^\*\d+$/
|
84
|
+
# Hinweis für Multiworder, dass Multiword mit (\d) Wörtern länge zu prüfen ist
|
85
|
+
str
|
86
|
+
when /^#(.)$/
|
87
|
+
# Alleinige Angabe der Wortklasse => Ergebniswort ist gleich dem Schlüssel
|
88
|
+
Lexical.new(key, $1)
|
89
|
+
when /^([^#]+?)\s*#(.)$/
|
90
|
+
# Angabe Ergebniswort und Wortklasse
|
91
|
+
Lexical.new($1, $2)
|
92
|
+
when /^([^#]+)$/
|
93
|
+
# Angabe Ergebniswort ohne Wortklasse
|
94
|
+
Lexical.new($1, @wordclass)
|
95
|
+
else
|
96
|
+
str
|
97
|
+
end
|
98
|
+
end.compact.sort.uniq unless record.nil?
|
99
|
+
|
100
|
+
# Ergebnis zurückgeben
|
101
|
+
inc('data found') unless record.nil?
|
102
|
+
store(key, record)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
class Dictionary
|
108
|
+
|
109
|
+
include Cachable
|
110
|
+
include Reportable
|
111
|
+
|
112
|
+
def initialize(config, lingo)
|
113
|
+
init_reportable
|
114
|
+
init_cachable
|
115
|
+
|
116
|
+
dictionary_config = lingo.dictionary_config
|
117
|
+
|
118
|
+
# Parameter prüfen
|
119
|
+
raise "Keine Sprach-Konfiguration angegeben!" if dictionary_config.nil?
|
120
|
+
raise "Keine Parameter angegeben!" if config.nil?
|
121
|
+
raise "Keine Datenquellen angegeben!" unless config.has_key?('source')
|
122
|
+
|
123
|
+
# Parameter auslesen
|
124
|
+
@all_sources = (config['mode'].nil? || config['mode'].downcase=='all')
|
125
|
+
|
126
|
+
@sources = config['source'].map { |src| LexicalHash.new(src, lingo) }
|
127
|
+
|
128
|
+
lingo.dictionaries << self
|
129
|
+
|
130
|
+
# Parameter aus de.lang:language/dictionary auslesen
|
131
|
+
@suffixes = []
|
132
|
+
@infixes = []
|
133
|
+
|
134
|
+
dictionary_config['suffix'].each {|arr|
|
135
|
+
typ, sufli = arr
|
136
|
+
typ.downcase!
|
137
|
+
sufli.split.each {|suf|
|
138
|
+
su, ex = suf.split('/')
|
139
|
+
fix = [Regexp.new(su+'$', 'i'), ex.nil? ? '*' : ex, typ]
|
140
|
+
(typ=='f' ? @infixes : @suffixes) << fix
|
141
|
+
}
|
142
|
+
} if dictionary_config.has_key?( 'suffix' )
|
143
|
+
end
|
144
|
+
|
145
|
+
def close
|
146
|
+
@sources.each(&:close)
|
147
|
+
end
|
148
|
+
|
149
|
+
def report
|
150
|
+
super.tap { |rep| @sources.each { |src| rep.update(src.report) } }
|
151
|
+
end
|
152
|
+
|
153
|
+
# _dic_.find_word( _aString_ ) -> _aNewWord_
|
154
|
+
#
|
155
|
+
# Erstellt aus dem String ein Wort und sucht nach diesem im Wörterbuch.
|
156
|
+
def find_word(string)
|
157
|
+
# Cache abfragen
|
158
|
+
key = string.downcase
|
159
|
+
if hit?(key)
|
160
|
+
inc('cache hits')
|
161
|
+
word = retrieve(key)
|
162
|
+
word.form = string
|
163
|
+
return word
|
164
|
+
end
|
165
|
+
|
166
|
+
word = Word.new(string, WA_UNKNOWN)
|
167
|
+
lexicals = select_with_suffix(string)
|
168
|
+
unless lexicals.empty?
|
169
|
+
word.lexicals = lexicals
|
170
|
+
word.attr = WA_IDENTIFIED
|
171
|
+
end
|
172
|
+
store(key, word)
|
173
|
+
end
|
174
|
+
|
175
|
+
def find_synonyms(obj)
|
176
|
+
# alle Lexicals des Wortes
|
177
|
+
lexis = obj.lexicals
|
178
|
+
lexis = [obj] if lexis.empty? && obj.attr==WA_UNKNOWN
|
179
|
+
# alle gefundenen Synonyme
|
180
|
+
synos = []
|
181
|
+
# multiworder optimization
|
182
|
+
key_ref = %r{\A#{Regexp.escape(KEY_REF)}\d+}o
|
183
|
+
|
184
|
+
lexis.each do |lex|
|
185
|
+
# Synonyme für Teile eines Kompositum ausschließen
|
186
|
+
next if obj.attr==WA_KOMPOSITUM && lex.attr!=LA_KOMPOSITUM
|
187
|
+
# Synonyme für Synonyme ausschließen
|
188
|
+
next if lex.attr==LA_SYNONYM
|
189
|
+
|
190
|
+
select(lex.form).each do |syn|
|
191
|
+
synos << syn unless syn =~ key_ref
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
synos
|
196
|
+
end
|
197
|
+
|
198
|
+
# _dic_.select( _aString_ ) -> _ArrayOfLexicals_
|
199
|
+
#
|
200
|
+
# Sucht alle Wörterbücher durch und gibt den ersten Treffer zurück (+mode = first+), oder alle Treffer (+mode = all+)
|
201
|
+
def select(string)
|
202
|
+
lexicals = []
|
203
|
+
|
204
|
+
@sources.each { |src|
|
205
|
+
if lexis = src[string]
|
206
|
+
lexicals += lexis
|
207
|
+
break unless @all_sources
|
208
|
+
end
|
209
|
+
}
|
210
|
+
|
211
|
+
lexicals.sort.uniq
|
212
|
+
end
|
213
|
+
|
214
|
+
# _dic_.select_with_suffix( _aString_ ) -> _ArrayOfLexicals_
|
215
|
+
#
|
216
|
+
# Sucht alle Wörterbücher durch und gibt den ersten Treffer zurück (+mode = first+), oder alle Treffer (+mode = all+).
|
217
|
+
# Sucht dabei auch Wörter, die um wortklassenspezifische Suffixe bereinigt wurden.
|
218
|
+
def select_with_suffix(string)
|
219
|
+
lexicals = select(string)
|
220
|
+
if lexicals.empty?
|
221
|
+
suffix_lexicals(string).each { |suflex|
|
222
|
+
select(suflex.form).each { |srclex|
|
223
|
+
lexicals << srclex if suflex.attr == srclex.attr
|
224
|
+
}
|
225
|
+
}
|
226
|
+
end
|
227
|
+
lexicals
|
228
|
+
end
|
229
|
+
|
230
|
+
# _dic_.select_with_infix( _aString_ ) -> _ArrayOfLexicals_
|
231
|
+
#
|
232
|
+
# Sucht alle Wörterbücher durch und gibt den ersten Treffer zurück (+mode = first+), oder alle Treffer (+mode = all+).
|
233
|
+
# Sucht dabei auch Wörter, die eine Fugung am Ende haben.
|
234
|
+
def select_with_infix(string)
|
235
|
+
lexicals = select(string)
|
236
|
+
if lexicals.size == 0
|
237
|
+
infix_lexicals(string).each { |inlex|
|
238
|
+
select(inlex.form).each { |srclex|
|
239
|
+
lexicals << srclex
|
240
|
+
}
|
241
|
+
}
|
242
|
+
end
|
243
|
+
lexicals
|
244
|
+
end
|
245
|
+
|
246
|
+
# _dic_.suffix_lexicals( _aString_ ) -> _ArrayOfLexicals_
|
247
|
+
#
|
248
|
+
# Gibt alle möglichen Lexicals zurück, die von der Endung her auf den String anwendbar sind:
|
249
|
+
#
|
250
|
+
# dic.suffix_lexicals("Hasens") -> [(hasen/s), (hasen/e), (has/e)]
|
251
|
+
def suffix_lexicals(string)
|
252
|
+
lexicals = []
|
253
|
+
newform = regex = ext = type = nil
|
254
|
+
@suffixes.each { |suf|
|
255
|
+
regex, ext, type = suf
|
256
|
+
if string =~ regex
|
257
|
+
newform = $`+((ext=="*")?'':ext)+$'
|
258
|
+
lexicals << Lexical.new(newform, type)
|
259
|
+
end
|
260
|
+
}
|
261
|
+
lexicals
|
262
|
+
end
|
263
|
+
|
264
|
+
# _dic_.gap_lexicals( _aString_ ) -> _ArrayOfLexicals_
|
265
|
+
#
|
266
|
+
# Gibt alle möglichen Lexicals zurück, die von der Endung her auf den String anwendbar sind:
|
267
|
+
def infix_lexicals(string)
|
268
|
+
lexicals = []
|
269
|
+
newform = regex = ext = type = nil
|
270
|
+
@infixes.each { |suf|
|
271
|
+
regex, ext, type = suf
|
272
|
+
if string =~ regex
|
273
|
+
newform = $`+((ext=="*")?'':ext)+$'
|
274
|
+
lexicals << Lexical.new(newform, type)
|
275
|
+
end
|
276
|
+
}
|
277
|
+
lexicals
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
class Compositum
|
283
|
+
end
|
284
|
+
|
285
|
+
# Die Klasse Grammar beinhaltet grammatikalische Spezialitäten einer Sprache. Derzeit findet die
|
286
|
+
# Kompositumerkennung hier ihren Platz, die mit der Methode find_compositum aufgerufen werden kann.
|
287
|
+
# Die Klasse Grammar wird genau wie ein Dictionary initialisiert. Das bei der Initialisierung angegebene Wörterbuch ist Grundlage
|
288
|
+
# für die Erkennung der Kompositumteile.
|
289
|
+
|
290
|
+
class Grammar
|
291
|
+
|
292
|
+
# Ergebnisse der Kompositumerkennung werden gespeichert und bei erneutem Aufruf mit gleichem Suchwort genutzt
|
293
|
+
include Cachable
|
294
|
+
|
295
|
+
# Die Verarbeitung wird statistisch erfasst und mit der Option -s angezeigt
|
296
|
+
include Reportable
|
297
|
+
|
298
|
+
# initialize(config, dictionary_config) -> _Grammar_
|
299
|
+
# config = Attendee-spezifische Parameter
|
300
|
+
# dictionary_config = Datenbankkonfiguration aus de.lang
|
301
|
+
def initialize(config, lingo)
|
302
|
+
init_reportable
|
303
|
+
init_cachable
|
304
|
+
|
305
|
+
@dictionary = Dictionary.new(config, lingo)
|
306
|
+
|
307
|
+
# Sprachspezifische Einstellungen für Kompositumverarbeitung laden (die nachfolgenden Werte können in der
|
308
|
+
# Konfigurationsdatei de.lang nach belieben angepasst werden)
|
309
|
+
comp = lingo.dictionary_config['compositum']
|
310
|
+
|
311
|
+
# Ein Wort muss mindestens 8 Zeichen lang sein, damit überhaupt eine Prüfung stattfindet.
|
312
|
+
@comp_min_word_size = (comp['min-word-size'] || '8').to_i
|
313
|
+
|
314
|
+
# Die durchschnittliche Länge der Kompositum-Wortteile muss mindestens 4 Zeichen lang sein, sonst ist es kein
|
315
|
+
# gültiges Kompositum.
|
316
|
+
@comp_min_avg_part_size = (comp['min-avg-part-size'] || '4').to_i
|
317
|
+
|
318
|
+
# Der kürzeste Kompositum-Wortteil muss mindestens 1 Zeichen lang sein
|
319
|
+
@comp_min_part_size = (comp['min-part-size'] || '1').to_i
|
320
|
+
|
321
|
+
# Ein Kompositum darf aus höchstens 4 Wortteilen bestehen
|
322
|
+
@comp_max_parts = (comp['max-parts'] || '4').to_i
|
323
|
+
|
324
|
+
# Die Wortklasse eines Kompositum-Wortteils kann separat gekennzeichnet werden, um sie von Wortklassen normaler Wörter
|
325
|
+
# unterscheiden zu können z.B. Hausmeister => ['haus/s', 'meister/s'] oder Hausmeister => ['haus/s+', 'meister/s+'] mit
|
326
|
+
# append-wordclass = '+'
|
327
|
+
@append_wc = comp.fetch( 'append-wordclass', '' )
|
328
|
+
|
329
|
+
# Bestimmte Sequenzen können als ungültige Komposita erkannt werden, z.B. ist ein Kompositum aus zwei Adjetiven kein
|
330
|
+
# Kompositum, also skip-sequence = 'aa'
|
331
|
+
@sequences = comp.fetch( 'skip-sequences', [] ).collect { |sq| sq.downcase }
|
332
|
+
|
333
|
+
# Liste der Vorschläge für eine Zerlegung
|
334
|
+
@suggestions = []
|
335
|
+
end
|
336
|
+
|
337
|
+
def close
|
338
|
+
@dictionary.close
|
339
|
+
end
|
340
|
+
|
341
|
+
alias_method :report_grammar, :report
|
342
|
+
|
343
|
+
def report
|
344
|
+
rep = report_grammar
|
345
|
+
rep.update(@dictionary.report)
|
346
|
+
rep
|
347
|
+
end
|
348
|
+
|
349
|
+
# find_compositum(string) -> word wenn level=1
|
350
|
+
# find_compositum(string) -> [lexicals, stats] wenn level!=1
|
351
|
+
#
|
352
|
+
# find_compositum arbeitet in verschiedenen Leveln, da die Methode auch rekursiv aufgerufen wird. Ein Level größer 1
|
353
|
+
# entspricht daher einem rekursiven Aufruf
|
354
|
+
def find_compositum(string, level=1, has_tail=false)
|
355
|
+
# Prüfen, ob string bereits auf Kompositum getestet wurde. Wenn ja, dann Ergebnis des letztes Aufrufs zurück geben.
|
356
|
+
key = string.downcase
|
357
|
+
if level == 1 && hit?(key)
|
358
|
+
inc('cache hits')
|
359
|
+
return retrieve(key)
|
360
|
+
end
|
361
|
+
|
362
|
+
# Ergebnis vorbelegen
|
363
|
+
comp = Word.new(string, WA_UNKNOWN)
|
364
|
+
|
365
|
+
# Validitätsprüfung: nur Strings mit Mindestlänge auf Kompositum prüfen
|
366
|
+
if string.size <= @comp_min_word_size
|
367
|
+
inc('String zu kurz')
|
368
|
+
return (level==1) ? comp : [[],[],'']
|
369
|
+
end
|
370
|
+
|
371
|
+
# Kompositumerkennung initialisieren
|
372
|
+
inc('Komposita geprüft')
|
373
|
+
stats, lexis, seqs = permute_compositum(string.downcase, level, has_tail)
|
374
|
+
|
375
|
+
if level==1
|
376
|
+
# Auf Level 1 Kompositum zurück geben
|
377
|
+
if lexis.size > 0 && is_valid?( string, stats, lexis, seqs )
|
378
|
+
inc('Komposita erkannt')
|
379
|
+
comp.attr = WA_KOMPOSITUM
|
380
|
+
comp.lexicals = lexis.collect do |lex|
|
381
|
+
(lex.attr==LA_KOMPOSITUM) ? lex : Lexical.new(lex.form, lex.attr+@append_wc)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
return store(key, comp)
|
386
|
+
end
|
387
|
+
|
388
|
+
# Validitätsprüfung
|
389
|
+
if lexis.size > 0 && is_valid?(string, stats, lexis, seqs)
|
390
|
+
[stats, lexis, seqs]
|
391
|
+
else
|
392
|
+
[[],[],'']
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
private
|
397
|
+
|
398
|
+
def is_valid?(string, stats, lexis, seqs)
|
399
|
+
is_valid = true
|
400
|
+
is_valid &&= (stats.size <= @comp_max_parts)
|
401
|
+
is_valid &&= (stats.sort[0] >= @comp_min_part_size)
|
402
|
+
is_valid &&= (string.size/stats.size) >= @comp_min_avg_part_size
|
403
|
+
is_valid &&= @sequences.index( seqs ).nil? unless @sequences.empty?
|
404
|
+
is_valid
|
405
|
+
end
|
406
|
+
|
407
|
+
# permute_string( _aString_ ) -> [lexicals, stats, seqs]
|
408
|
+
def permute_compositum(string, level, has_tail)
|
409
|
+
@suggestions[level] = [] if @suggestions[level].nil?
|
410
|
+
|
411
|
+
# Finde letzten Bindesstrich im Wort
|
412
|
+
if string =~ /^(.+)-([^-]+)$/
|
413
|
+
test_compositum($1, '-', $2, level, has_tail)
|
414
|
+
else
|
415
|
+
length = string.length
|
416
|
+
|
417
|
+
# Wortteilungen testen
|
418
|
+
1.upto(length - 1) do |p|
|
419
|
+
# String teilen und testen
|
420
|
+
fr_str, ba_str = string.slice(0...p), string.slice(p...length)
|
421
|
+
stats, lexis, seqs = test_compositum(fr_str, '', ba_str, level, has_tail)
|
422
|
+
|
423
|
+
unless lexis.empty?
|
424
|
+
if lexis[-1].attr==LA_TAKEITASIS
|
425
|
+
# => halbes Kompositum
|
426
|
+
@suggestions[level] << [stats, lexis, seqs]
|
427
|
+
else
|
428
|
+
# => ganzes Kompositum
|
429
|
+
return [stats, lexis, seqs]
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# alle Wortteilungen durchprobiert und noch immer kein definitives Kompositum erkannt. Dann nehme besten Vorschlag.
|
435
|
+
if @suggestions[level].empty?
|
436
|
+
[[],[],'']
|
437
|
+
else
|
438
|
+
stats, lexis, seqs = @suggestions[level][0]
|
439
|
+
@suggestions[level].clear
|
440
|
+
[stats, lexis, seqs]
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
# test_compositum() -> [stats, lexicals, seq]
|
446
|
+
#
|
447
|
+
# Testet einen definiert zerlegten String auf Kompositum
|
448
|
+
def test_compositum(front_string, infix, back_string, level, has_tail)
|
449
|
+
# Statistik merken für Validitätsprüfung
|
450
|
+
stats = [front_string.size, back_string.size]
|
451
|
+
seqs = ['?', '?']
|
452
|
+
|
453
|
+
# zuerst hinteren Teil auflösen
|
454
|
+
# 1. Möglichkeit: Wort mit oder ohne Suffix
|
455
|
+
back_lexicals = @dictionary.select_with_suffix(back_string)
|
456
|
+
unless back_lexicals.empty?
|
457
|
+
back_form = has_tail ? back_string : back_lexicals.sort[0].form
|
458
|
+
seqs[1] = back_lexicals.sort[0].attr
|
459
|
+
end
|
460
|
+
|
461
|
+
# 2. Möglichkeit: Wort mit oder ohne Infix, wenn es nicht der letzte Teil des Wortes ist
|
462
|
+
if back_lexicals.empty? && has_tail
|
463
|
+
back_lexicals = @dictionary.select_with_infix(back_string)
|
464
|
+
unless back_lexicals.empty?
|
465
|
+
back_form = back_string
|
466
|
+
seqs[1] = back_lexicals.sort[0].attr
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
# 3. Möglichkeit: Selber ein Kompositum (nur im Bindestrich-Fall!)
|
471
|
+
if back_lexicals.empty? && infix=='-'
|
472
|
+
back_stats, back_lexicals, back_seqs = find_compositum(back_string, level+1, has_tail)
|
473
|
+
unless back_lexicals.empty?
|
474
|
+
back_form = back_lexicals.sort[0].form
|
475
|
+
seqs[1] = back_seqs
|
476
|
+
stats = stats[0..0] + back_stats
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
# 4. Möglichkeit: Take it as is [Nimm's, wie es ist] (nur im Bindestrich-Fall!)
|
481
|
+
if back_lexicals.empty? && infix=='-'
|
482
|
+
back_lexicals = [Lexical.new(back_string, LA_TAKEITASIS)]
|
483
|
+
back_form = back_string
|
484
|
+
seqs[1] = back_lexicals.sort[0].attr
|
485
|
+
end
|
486
|
+
|
487
|
+
# wenn immer noch nicht erkannt, dann sofort zurück
|
488
|
+
return [[],[],''] if back_lexicals.empty?
|
489
|
+
|
490
|
+
# dann vorderen Teil auflösen
|
491
|
+
#
|
492
|
+
# 1. Möglichkeit: Wort mit oder ohne Infix
|
493
|
+
front_lexicals = @dictionary.select_with_infix(front_string)
|
494
|
+
unless front_lexicals.empty?
|
495
|
+
front_form = front_string
|
496
|
+
seqs[0] = front_lexicals.sort[0].attr
|
497
|
+
end
|
498
|
+
|
499
|
+
# 2. Möglichkeit: Selber ein Kompositum
|
500
|
+
if front_lexicals.empty?
|
501
|
+
front_stats, front_lexicals, front_seqs = find_compositum(front_string, level+1, true)
|
502
|
+
unless front_lexicals.empty?
|
503
|
+
front_form = front_lexicals.sort[0].form
|
504
|
+
seqs[0] = front_seqs
|
505
|
+
stats = front_stats + stats[1..-1]
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# 3. Möglichkeit: Take it as is [Nimm's, wie es ist] (nur im Bindestrich-Fall!)
|
510
|
+
if front_lexicals.empty? && infix=='-'
|
511
|
+
front_lexicals = [Lexical.new(front_string, LA_TAKEITASIS)]
|
512
|
+
seqs[0] = front_lexicals.sort[0].attr
|
513
|
+
front_form = front_string
|
514
|
+
end
|
515
|
+
|
516
|
+
# wenn immer noch nicht erkannt, dann sofort zurück
|
517
|
+
return [[],[],''] if front_lexicals.empty?
|
518
|
+
|
519
|
+
# Kompositum gefunden, Grundform bilden
|
520
|
+
lexis = (front_lexicals + back_lexicals).collect { |lex|
|
521
|
+
(lex.attr==LA_KOMPOSITUM) ? nil : lex
|
522
|
+
}.compact
|
523
|
+
lexis << Lexical.new(front_form + infix + back_form, LA_KOMPOSITUM)
|
524
|
+
|
525
|
+
[stats, lexis.sort, seqs.join ]
|
526
|
+
end
|
527
|
+
|
528
|
+
end
|
529
|
+
|
530
|
+
end
|