wordnet 0.0.5 → 1.0.0.pre.126

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gemtest +0 -0
  2. data/History.rdoc +5 -0
  3. data/LICENSE +9 -9
  4. data/Manifest.txt +39 -0
  5. data/README.rdoc +60 -0
  6. data/Rakefile +47 -267
  7. data/TODO +9 -0
  8. data/WordNet30-license.txt +31 -0
  9. data/examples/add-laced-boots.rb +35 -0
  10. data/examples/clothes-with-collars.rb +42 -0
  11. data/examples/clothesWithTongues.rb +0 -0
  12. data/examples/domainTree.rb +0 -0
  13. data/examples/memberTree.rb +0 -0
  14. data/lib/wordnet/constants.rb +259 -296
  15. data/lib/wordnet/lexicallink.rb +34 -0
  16. data/lib/wordnet/lexicon.rb +158 -386
  17. data/lib/wordnet/mixins.rb +62 -0
  18. data/lib/wordnet/model.rb +78 -0
  19. data/lib/wordnet/morph.rb +25 -0
  20. data/lib/wordnet/semanticlink.rb +52 -0
  21. data/lib/wordnet/sense.rb +55 -0
  22. data/lib/wordnet/sumoterm.rb +21 -0
  23. data/lib/wordnet/synset.rb +404 -859
  24. data/lib/wordnet/utils.rb +126 -0
  25. data/lib/wordnet/word.rb +119 -0
  26. data/lib/wordnet.rb +113 -76
  27. data/spec/lib/helpers.rb +102 -133
  28. data/spec/linguawordnet.tests.rb +38 -0
  29. data/spec/wordnet/lexicon_spec.rb +96 -186
  30. data/spec/wordnet/model_spec.rb +59 -0
  31. data/spec/wordnet/semanticlink_spec.rb +42 -0
  32. data/spec/wordnet/synset_spec.rb +27 -256
  33. data/spec/wordnet/word_spec.rb +58 -0
  34. data/spec/wordnet_spec.rb +52 -0
  35. data.tar.gz.sig +0 -0
  36. metadata +227 -188
  37. metadata.gz.sig +0 -0
  38. data/ChangeLog +0 -720
  39. data/README +0 -93
  40. data/Rakefile.local +0 -46
  41. data/convertdb.rb +0 -417
  42. data/examples/addLacedBoots.rb +0 -27
  43. data/examples/clothesWithCollars.rb +0 -36
  44. data/rake/dependencies.rb +0 -76
  45. data/rake/helpers.rb +0 -384
  46. data/rake/manual.rb +0 -755
  47. data/rake/packaging.rb +0 -112
  48. data/rake/publishing.rb +0 -303
  49. data/rake/rdoc.rb +0 -35
  50. data/rake/style.rb +0 -62
  51. data/rake/svn.rb +0 -469
  52. data/rake/testing.rb +0 -192
  53. data/rake/verifytask.rb +0 -64
  54. data/utils.rb +0 -838
@@ -1,908 +1,453 @@
1
1
  #!/usr/bin/ruby
2
- #
2
+
3
+ require 'wordnet' unless defined?( WordNet )
4
+ require 'wordnet/constants'
5
+ require 'wordnet/model'
6
+
7
+
3
8
  # WordNet synonym-set object class
4
- #
5
- # == Synopsis
6
- #
7
- # ss = lexicon.lookupSynset( "word", WordNet::Noun, 1 )
8
- # puts "Definition: %s" % ss.gloss
9
- # coords = ss.coordinates
10
9
  #
11
- # == Description
12
- #
13
10
  # Instances of this class encapsulate the data for a synonym set ('synset') in a
14
- # Wordnet lexical database. A synonym set is a set of words that are
11
+ # WordNet lexical database. A synonym set is a set of words that are
15
12
  # interchangeable in some context.
16
- #
17
- # == Author
18
13
  #
19
- # Michael Granger <ged@FaerieMUD.org>
20
- #
21
- # Copyright (c) 2002-2008 The FaerieMUD Consortium. All rights reserved.
22
- #
23
- # This module is free software. You may use, modify, and/or redistribute this
24
- # software under the terms of the Perl Artistic License. (See
25
- # http://language.perl.com/misc/Artistic.html)
26
- #
27
- # Much of this code was inspired by/ported from the Lingua::Wordnet Perl module
28
- # by Dan Brian.
29
- #
30
- # == Version
14
+ # ss = WordNet::Synset[ 106286395 ]
15
+ # # => #<WordNet::Synset @values={:synsetid=>106286395, :pos=>"n",
16
+ # :lexdomainid=>10,
17
+ # :definition=>"a unit of language that native speakers can identify"}>
31
18
  #
32
- # $Id: synset.rb 90 2008-07-09 23:02:53Z deveiant $
33
- #
34
-
35
- require 'sync'
36
- require 'wordnet/constants'
37
-
38
- module WordNet
39
-
40
- ### Synset internal error class
41
- class SynsetError < StandardError ; end
42
-
43
- ### "Synonym set" class - encapsulates the data for a set of words in the
44
- ### lexical database that are interchangeable in some context, and provides
45
- ### methods for accessing its relationships.
46
- class Synset
47
- include WordNet::Constants
48
- include CrossCase if defined?( CrossCase )
49
-
50
- # Subversion ID
51
- SVNId = %q$Id: synset.rb 90 2008-07-09 23:02:53Z deveiant $
52
-
53
- # Subversion Rev
54
- SVNRev = %q$Rev: 90 $
55
-
56
- # The "pointer" type that encapsulates relationships between one synset
57
- # and another.
58
- class Pointer
59
- include WordNet::Constants
60
- include CrossCase if defined?( CrossCase )
61
-
62
- #########################################################
63
- ### C L A S S M E T H O D S
64
- #########################################################
65
-
66
- ### Make an Array of WordNet::Synset::Pointer objects out of the
67
- ### given +pointerList+. The pointerlist is a string of pointers
68
- ### delimited by Constants::SUB_DELIM. Pointers are in the form:
69
- ### "<pointer_symbol> <synset_offset>%<pos> <source/target>"
70
- def self::parse( pointerString )
71
- type, offsetPos, ptrNums = pointerString.split(/\s+/)
72
- offset, pos = offsetPos.split( /%/, 2 )
73
- new( type, offset, pos, ptrNums[0,2], ptrNums[2,2] )
74
- end
75
-
76
-
77
- #########################################################
78
- ### I N S T A N C E M E T H O D S
79
- #########################################################
80
-
81
- ### Create a new synset pointer with the given arguments. The
82
- ### +ptrType+ is the type of the link between synsets, and must be
83
- ### either a key or a value of WordNet::Constants::POINTER_TYPES. The
84
- ### +offset+ is the unique identifier of the target synset, and
85
- ### +pos+ is its part-of-speech, which must be either a key or value
86
- ### of WordNet::Constants::SYNTACTIC_CATEGORIES. The +source_wn+ and
87
- ### +target_wn+ are numerical values which distinguish lexical and
88
- ### semantic pointers. +source_wn+ indicates the word number in the
89
- ### current (source) synset, and +target_wn+ indicates the word
90
- ### number in the target synset. If both are 0 (the default) it
91
- ### means that the pointer type of the pointer represents a semantic
92
- ### relation between the current (source) synset and the target
93
- ### synset indicated by +offset+.
94
- def initialize( type, offset, pos=Noun, source_wn=0, target_wn=0 )
95
-
96
- # Allow type = '!', 'antonym', or :antonym. Also handle
97
- # splitting of compound pointers (e.g., :memberMeronym / '%m')
98
- # into their correct type/subtype parts.
99
- @type = @subtype = nil
100
- if type.to_s.length == 1
101
- @type = POINTER_SYMBOLS[ type[0,1] ]
102
-
103
- elsif type.to_s.length == 2
104
- @type = POINTER_SYMBOLS[ type[0,1] ]
105
- raise "No known subtypes for '%s'" % [@type] unless
106
- POINTER_SUBTYPES.key?( @type )
107
- @subtype = POINTER_SUBTYPES[ @type ].index( type ) or
108
- raise "Unknown subtype '%s' for '%s'" %
109
- [ type, @type ]
110
-
111
- else
112
- if POINTER_TYPES.key?( type.to_sym )
113
- @type = type.to_sym
114
- elsif /([a-z]+)([A-Z][a-z]+)/ =~ type.to_s
115
- subtype, maintype = $1, $2.downcase
116
- @type = maintype.to_sym if
117
- POINTER_TYPES.key?( maintype.to_sym )
118
- @subtype = subtype.to_sym
119
- end
120
- end
121
-
122
- raise ArgumentError, "No such pointer type %p" % type if
123
- @type.nil?
124
-
125
- # Allow pos = 'n', 'noun', or :noun
126
- @part_of_speech = nil
127
- if pos.to_s.length == 1
128
- @part_of_speech = SYNTACTIC_SYMBOLS[ pos ]
129
- else
130
- @part_of_speech = pos.to_sym if
131
- SYNTACTIC_CATEGORIES.key?( pos.to_sym )
132
- end
133
- raise ArgumentError, "No such part of speech %p" % pos if
134
- @part_of_speech.nil?
135
-
136
- # Other attributes
137
- @offset = offset
138
- @source_wn = source_wn
139
- @target_wn = target_wn
140
- end
141
-
142
-
143
- ######
144
- public
145
- ######
146
-
147
- # The type of the pointer. Will be one of the keys of
148
- # WordNet::POINTER_TYPES (e.g., :meronym).
149
- attr_accessor :type
150
-
151
- # The subtype of the pointer, if any. Will be one of the keys of one
152
- # of the hashes in POINTER_SUBTYPES (e.g., :portion).
153
- attr_accessor :subtype
154
-
155
- # The offset of the target synset
156
- attr_accessor :offset
157
-
158
- # The part-of-speech of the target synset. Will be one of the keys
159
- # of WordNet::SYNTACTIC_CATEGORIES.
160
- attr_accessor :part_of_speech
161
-
162
- # The word number in the source synset
163
- attr_accessor :source_wn
164
-
165
- # The word number in the target synset
166
- attr_accessor :target_wn
167
-
168
-
169
- ### Return the Pointer as a human-readable String suitable for
170
- ### debugging.
171
- def inspect
172
- "#<%s:0x%08x %s %s>" % [
173
- self.class.name,
174
- self.object_id,
175
- @subtype ? "#@type(#@subtype)" : @type,
176
- self.synset,
177
- ]
178
- end
179
-
180
-
181
- ### Return the synset key of the target synset (i.e.,
182
- ### <offset>%<pos symbol>).
183
- def synset
184
- self.offset + "%" + self.pos
185
- end
186
-
187
-
188
- ### Return the syntactic category symbol for this pointer
189
- def pos
190
- return SYNTACTIC_CATEGORIES[ @part_of_speech ]
191
- end
192
-
193
-
194
- ### Return the pointer type symbol for this pointer
195
- def type_symbol
196
- unless @subtype
197
- return POINTER_TYPES[ @type ]
198
- else
199
- return POINTER_SUBTYPES[ @type ][ @subtype ]
200
- end
201
- end
202
-
203
-
204
- ### Comparison operator. Pointer are equivalent if they point at the
205
- ### same synset and are of the same type.
206
- def ==( other )
207
- return false unless other.is_a?( self.class )
208
- other.offset == self.offset &&
209
- other.type == self.type
210
- end
211
-
212
-
213
- ### Return the pointer in its stringified form.
214
- def to_s
215
- "%s %d%%%s %02x%02x" % [
216
- ptr.type_symbol,
217
- ptr.offset,
218
- ptr.posSymbol,
219
- ptr.source_wn,
220
- ptr.target_wn,
221
- ]
222
- end
223
- end # class Pointer
224
-
225
-
226
- #############################################################
227
- ### C L A S S M E T H O D S
228
- #############################################################
229
-
230
- ### Define a group of pointer methods based on +symbol+ that will fetch,
231
- ### add, and delete pointer synsets of the type indicated. If no pointer
232
- ### type corresponding to the given +symbol+ is found, a variant without
233
- ### a trailing 's' is tried (e.g., 'def_pointer_methods :antonyms' will
234
- ### create methods called #antonyms and #antonyms=, but will fetch
235
- ### pointers of type :antonym). If the pointer type has subtypes
236
- ### (according to WordNet::POINTER_SUBTYPES), accessors/mutators for the
237
- ### subtypes will be generated as well.
238
- def self::def_pointer_methods( symbol ) # :nodoc:
239
- name = symbol.to_s
240
- casename = name.dup
241
- casename[ 0,1 ] = casename[ 0,1 ].upcase
242
- type = nil
243
- $stderr.puts '-' * 50,
244
- ">>> defining pointer methods for %p" % [symbol] if $DEBUG
245
-
246
- if POINTER_TYPES.key?( symbol )
247
- type = symbol
248
- elsif POINTER_TYPES.key?( symbol.to_s.sub(/s$/, '').to_sym )
249
- type = symbol.to_s.sub(/s$/, '').to_sym
250
- else
251
- raise ArgumentError, "Unknown pointer type %p" % symbol
252
- end
253
-
254
- # Define the accessor
255
- $stderr.puts "Defining accessors for %p" % [ type ] if $DEBUG
256
- define_method( name.to_sym ) { self.fetch_synset_pointers(type) }
257
- define_method( "#{name}=".to_sym ) do |*synsets|
258
- self.set_synset_pointers( type, synsets, nil )
259
- end
260
-
261
- # If the pointer is one that has subtypes, make the variants list
262
- # out of the subtypes. If it doesn't have subtypes, make the only
263
- # variant nil, which will cause the mutators to be defined for the
264
- # main pointer type.
265
- if POINTER_SUBTYPES.key?( type )
266
- variants = POINTER_SUBTYPES[ type ].keys
267
- else
268
- variants = [nil]
269
- end
270
-
271
- # Define a set of methods for each variant, or for the main method
272
- # if the variant is nil.
273
- variants.each do |subtype|
274
- varname = subtype ? [subtype, name].join('_') : name
275
-
276
- unless subtype.nil?
277
- $stderr.puts "Defining reader for #{varname}" if $DEBUG
278
- define_method( varname ) do
279
- self.fetch_synset_pointers( type, subtype )
280
- end
281
- else
282
- $stderr.puts "No subtype for %s (subtype = %p)" %
283
- [ varname, subtype ] if $DEBUG
284
- end
285
-
286
- $stderr.puts "Defining mutator for #{varname}" if $DEBUG
287
- define_method( "#{varname}=" ) do |*synsets|
288
- self.set_synset_pointers( type, synsets, subtype )
289
- end
290
- end
291
- end
292
-
293
-
294
- #############################################################
295
- ### I N S T A N C E M E T H O D S
296
- #############################################################
297
-
298
- ### Create a new Synset object in the specified +lexicon+ for the
299
- ### specified +word+ and +part_of_speech+. If +data+ is specified,
300
- ### initialize the synset's other object data from it. This method
301
- ### shouldn't be called directly: you should use one of the Lexicon
302
- ### class's factory methods: #create_synset, #lookup_synsets, or
303
- ### #lookup_synsetsByOffset.
304
- def initialize( lexicon, offset, pos, word=nil, data=nil )
305
- @lexicon = lexicon or
306
- raise ArgumentError, "%p is not a WordNet::Lexicon" % lexicon
307
- @part_of_speech = SYNTACTIC_SYMBOLS[ pos ] or
308
- raise ArgumentError, "No such part of speech %p" % pos
309
- @mutex = Sync::new
310
- @pointers = []
311
-
312
- if data
313
- @offset = offset.to_i
314
- @filenum, @wordlist, @pointerlist,
315
- @frameslist, @gloss = data.split( DELIM_RE )
316
- else
317
- @offset = 1
318
- @wordlist = word ? word : ''
319
- @filenum, @pointerlist, @frameslist, @gloss = [''] * 4
320
- end
321
- end
322
-
323
-
324
- ######
325
- public
326
- ######
327
-
328
- # The WordNet::Lexicon that was used to look up this synset
329
- attr_reader :lexicon
330
-
331
- # The syntactic category of this Synset. Will be one of "n" (noun), "v"
332
- # (verb), "a" (adjective), "r" (adverb), or "s" (other).
333
- attr_accessor :part_of_speech
334
-
335
- # The original byte offset of the synset in the data file; acts as the
336
- # unique identifier (when combined with #part_of_speech) of this Synset in
337
- # the database.
338
- attr_accessor :offset
339
-
340
- # The number corresponding to the lexicographer file name containing the
341
- # synset. Calling #lexInfo will return the actual filename. See the
342
- # "System Description" of wngloss(7WN) for more info about this.
343
- attr_accessor :filenum
344
-
345
- # The raw list of word/lex_id pairs associated with this synset. Each
346
- # word and lex_id is separated by a '%' character, and each pair is
347
- # delimited with a '|'. E.g., the wordlist for "animal" is:
348
- # "animal%0|animate_being%0|beast%0|brute%1|creature%0|fauna%1"
349
- attr_accessor :wordlist
350
-
351
- # The list of raw pointers to related synsets. E.g., the pointerlist for
352
- # "mourning dove" is:
353
- # "@ 01731700%n 0000|#m 01733452%n 0000"
354
- attr_accessor :pointerlist
355
-
356
- # The list of raw verb sentence frames for this synset.
357
- attr_accessor :frameslist
358
-
359
- # Definition and/or example sentences for the Synset.
360
- attr_accessor :gloss
361
-
362
-
363
- ### Return a human-readable representation of the Synset suitable for
364
- ### debugging.
365
- def inspect
366
- pointer_counts = self.pointer_map.collect {|type,ptrs|
367
- "#{type}s: #{ptrs.length}"
368
- }.join( ", " )
369
-
370
- %q{#<%s:0x%08x/%s %s (%s): "%s" (%s)>} % [
371
- self.class.name,
372
- self.object_id * 2,
373
- self.offset,
374
- self.words.join(", "),
375
- self.part_of_speech,
376
- self.gloss,
377
- pointer_counts,
378
- ]
379
- end
380
-
381
-
382
- ### Returns the Synset's unique identifier, made up of its offset and
383
- ### syntactic category catenated together with a '%' symbol.
384
- def key
385
- "%d%%%s" % [ self.offset, self.pos ]
386
- end
387
-
388
-
389
- ### The symbol which represents this synset's syntactic category. Will
390
- ### be one of :noun, :verb, :adjective, :adverb, or :other.
391
- def pos
392
- return SYNTACTIC_CATEGORIES[ @part_of_speech ]
393
- end
394
-
395
-
396
- ### Return each of the sentences of the gloss for this synset as an
397
- ### array. The gloss is a definition of the synset, and optionally one
398
- ### or more example sentences.
399
- def glosses
400
- return self.gloss.split( /\s*;\s*/ )
401
- end
402
-
403
-
404
- ### Returns true if the receiver and otherSyn are identical according to
405
- ### their offsets.
406
- def ==( otherSyn )
407
- return false unless otherSyn.kind_of?( WordNet::Synset )
408
- return self.offset == otherSyn.offset
19
+ # ss.words.map( &:lemma )
20
+ # # => ["word"]
21
+ #
22
+ # ss.hypernyms
23
+ # # => [#<WordNet::Synset @values={:synsetid=>106284225, :pos=>"n",
24
+ # :lexdomainid=>10,
25
+ # :definition=>"one of the natural units into which [...]"}>]
26
+ #
27
+ # ss.hyponyms
28
+ # # => [#<WordNet::Synset @values={:synsetid=>106287620, :pos=>"n",
29
+ # :lexdomainid=>10,
30
+ # :definition=>"a word or phrase spelled by rearranging [...]"}>,
31
+ # #<WordNet::Synset @values={:synsetid=>106287859, :pos=>"n",
32
+ # :lexdomainid=>10,
33
+ # :definition=>"a word (such as a pronoun) used to avoid [...]"}>,
34
+ # #<WordNet::Synset @values={:synsetid=>106288024, :pos=>"n",
35
+ # :lexdomainid=>10,
36
+ # :definition=>"a word that expresses a meaning opposed [...]"}>,
37
+ # ...
38
+ # ]
39
+ #
40
+ class WordNet::Synset < WordNet::Model( :synsets )
41
+ include WordNet::Constants
42
+
43
+ require 'wordnet/lexicallink'
44
+ require 'wordnet/semanticlink'
45
+
46
+ # Semantic link type keys; maps what the API calls them to what
47
+ # they are in the DB.
48
+ SEMANTIC_TYPEKEYS = Hash.new {|h,type| h[type] = type.to_s.chomp('s').to_sym }
49
+
50
+ # Now set the ones that aren't just the API name with
51
+ # the 's' at the end removed.
52
+ SEMANTIC_TYPEKEYS.merge!(
53
+ also_see: :also,
54
+ domain_categories: :domain_category,
55
+ domain_member_categories: :domain_member_category,
56
+ entailments: :entail,
57
+ similar_words: :similar,
58
+ )
59
+
60
+
61
+ set_primary_key :synsetid
62
+
63
+ ##
64
+ # :singleton-method:
65
+ # The WordNet::Words associated with the receiver
66
+ many_to_many :words,
67
+ :join_table => :senses,
68
+ :left_key => :synsetid,
69
+ :right_key => :wordid
70
+
71
+
72
+ ##
73
+ # :singleton-method:
74
+ # The WordNet::Senses associated with the receiver
75
+ one_to_many :senses,
76
+ :key => :synsetid,
77
+ :primary_key => :synsetid
78
+
79
+
80
+ ##
81
+ # :singleton-method:
82
+ # The WordNet::SemanticLinks indicating a relationship with other
83
+ # WordNet::Synsets
84
+ one_to_many :semlinks,
85
+ :class => :"WordNet::SemanticLink",
86
+ :key => :synset1id,
87
+ :primary_key => :synsetid,
88
+ :eager => :target
89
+
90
+
91
+ ##
92
+ # :singleton-method:
93
+ # The WordNet::SemanticLinks pointing *to* this Synset
94
+ many_to_one :semlinks_to,
95
+ :class => :"WordNet::SemanticLink",
96
+ :key => :synsetid,
97
+ :primary_key => :synset2id
98
+
99
+
100
+ ##
101
+ # :singleton-method:
102
+ # Terms from the Suggested Upper Merged Ontology
103
+ many_to_many :sumo_terms,
104
+ :join_table => :sumomaps,
105
+ :left_key => :synsetid,
106
+ :right_key => :sumoid
107
+
108
+
109
+ #################################################################
110
+ ### C L A S S M E T H O D S
111
+ #################################################################
112
+
113
+ # Cached lookup tables (lazy-loaded)
114
+ @lexdomain_table = nil
115
+ @lexdomains = nil
116
+ @linktype_table = nil
117
+ @linktypes = nil
118
+ @postype_table = nil
119
+ @postypes = nil
120
+
121
+
122
+ #
123
+ # :section: Dataset Methods
124
+ # This is a set of methods that return a Sequel::Dataset for Synsets pre-filtered
125
+ # by a certain criteria. They can be used to do stuff like:
126
+ #
127
+ # lexicon[ :language ].synsets_dataset.nouns
128
+ #
129
+
130
+ ##
131
+ # :singleton-method: nouns
132
+ # Dataset method: filtered by part of speech: nouns.
133
+ def_dataset_method( :nouns ) { filter(pos: 'n') }
134
+
135
+ ##
136
+ # :singleton-method: verbs
137
+ # Dataset method: filtered by part of speech: verbs.
138
+ def_dataset_method( :verbs ) { filter(pos: 'v') }
139
+
140
+ ##
141
+ # :singleton-method: adjectives
142
+ # Dataset method: filtered by part of speech: adjectives.
143
+ def_dataset_method( :adjectives ) { filter(pos: 'a') }
144
+
145
+ ##
146
+ # :singleton-method: adverbs
147
+ # Dataset method: filtered by part of speech: adverbs.
148
+ def_dataset_method( :adverbs ) { filter(pos: 'r') }
149
+
150
+ ##
151
+ # :singleton-method: adjective_satellites
152
+ # Dataset method: filtered by part of speech: adjective satellites.
153
+ def_dataset_method( :adjective_satellites ) { filter(pos: 's') }
154
+
155
+
156
+ ### Overridden to reset any lookup tables that may have been loaded from the previous
157
+ ### database.
158
+ def self::db=( newdb )
159
+ self.reset_lookup_tables
160
+ super
161
+ end
162
+
163
+
164
+ ### Unload all of the cached lookup tables that have been loaded.
165
+ def self::reset_lookup_tables
166
+ @lexdomain_table = nil
167
+ @lexdomains = nil
168
+ @linktype_table = nil
169
+ @linktypes = nil
170
+ @postype_table = nil
171
+ @postypes = nil
172
+ end
173
+
174
+
175
+ ### Return the table of lexical domains, keyed by id.
176
+ def self::lexdomain_table
177
+ @lexdomain_table ||= self.db[:lexdomains].to_hash( :lexdomainid )
178
+ end
179
+
180
+
181
+ ### Lexical domains, keyed by name as a String (e.g., "verb.cognition")
182
+ def self::lexdomains
183
+ @lexdomains ||= self.lexdomain_table.inject({}) do |hash,(id,domain)|
184
+ hash[ domain[:lexdomainname] ] = domain
185
+ hash
409
186
  end
187
+ end
410
188
 
411
189
 
412
-
413
- ### Returns an Array of words and/or collocations associated with this
414
- ### synset.
415
- def words
416
- @mutex.synchronize( Sync::SH ) {
417
- self.wordlist.split( SUB_DELIM_RE ).collect do |word|
418
- word.gsub( /_/, ' ' ).sub( /%.*$/, '' )
419
- end
190
+ ### Return the table of link types, keyed by linkid
191
+ def self::linktype_table
192
+ @linktype_table ||= self.db[:linktypes].inject({}) do |hash,row|
193
+ hash[ row[:linkid] ] = {
194
+ :id => row[:linkid],
195
+ :typename => row[:link],
196
+ :type => row[:link].gsub( /\s+/, '_' ).to_sym,
197
+ :recurses => row[:recurses].nonzero? ? true : false,
420
198
  }
199
+ hash
421
200
  end
422
- alias_method :synonyms, :words
201
+ end
423
202
 
424
203
 
425
- ### Set the words in this synset's wordlist to +newWords+
426
- def words=( *newWords )
427
- @mutex.synchronize( Sync::EX ) {
428
- @wordlist = newWords.join( SUB_DELIM )
429
- }
204
+ ### Return the table of link types, keyed by name.
205
+ def self::linktypes
206
+ @linktypes ||= self.linktype_table.inject({}) do |hash,(id,link)|
207
+ hash[ link[:type] ] = link
208
+ hash
430
209
  end
210
+ end
431
211
 
432
212
 
433
- ### Add the specified +newWords+ to this synset's wordlist. Alias:
434
- ### +add_words+.
435
- def add_words( *newWords )
436
- @mutex.synchronize( Sync::EX ) {
437
- self.words |= newWords
438
- }
213
+ ### Return the table of part-of-speech types, keyed by letter identifier.
214
+ def self::postype_table
215
+ @postype_table ||= self.db[:postypes].inject({}) do |hash, row|
216
+ hash[ row[:pos].untaint.to_sym ] = row[:posname]
217
+ hash
439
218
  end
219
+ end
440
220
 
441
221
 
442
- ### Delete the specified +oldWords+ from this synset's wordlist. Alias:
443
- ### +delete_words+.
444
- def delete_words( *oldWords )
445
- @mutex.synchronize( Sync::EX ) {
446
- self.words -= oldWords
447
- }
448
- end
222
+ ### Return the table of part-of-speech names to letter identifiers (both Symbols).
223
+ def self::postypes
224
+ @postypes ||= self.postype_table.invert
225
+ end
449
226
 
450
227
 
451
- ### Return the synset as a string. Alias: +overview+.
452
- def to_s
453
- @mutex.synchronize( Sync::SH ) {
454
- wordlist = self.words.join(", ").gsub( /%\d/, '' ).gsub( /_/, ' ' )
455
- return "#{wordlist} [#{self.part_of_speech}] -- (#{self.gloss})"
456
- }
457
- end
458
- alias_method :overview, :to_s
459
-
228
+ ### Generate methods that will return Synsets related by the given semantic pointer
229
+ ### +type+.
230
+ def self::semantic_link( type )
231
+ WordNet.log.debug "Generating a %p method" % [ type ]
460
232
 
461
- ### Writes any changes made to the object to the database and updates all
462
- ### affected synset data and indexes. If the object passes out of scope
463
- ### before #write is called, the changes are lost.
464
- def store
465
- @mutex.synchronize( Sync::EX ) {
466
- self.lexicon.store_synset( self )
467
- }
233
+ ds_method_body = Proc.new do
234
+ self.semanticlink_dataset( type )
468
235
  end
469
- alias_method :write, :store
470
-
236
+ define_method( "#{type}_dataset", &ds_method_body )
471
237
 
472
- ### Removes this synset from the database.
473
- def remove
474
- @mutex.synchronize( Sync::EX ) {
475
- self.lexicon.remove_synset( self )
476
- }
238
+ ss_method_body = Proc.new do
239
+ self.semanticlink_dataset( type ).all
477
240
  end
241
+ define_method( type, &ss_method_body )
242
+ end
478
243
 
479
244
 
480
- ### Returns the synset's data in a form suitable for storage in the
481
- ### lexicon's database.
482
- def serialize
483
- @mutex.synchronize( Sync::SH ) {
484
- return [
485
- @filenum,
486
- @wordlist,
487
- @pointerlist,
488
- @frameslist,
489
- @gloss
490
- ].join( WordNet::DELIM )
491
- }
492
- end
245
+ ######
246
+ public
247
+ ######
493
248
 
249
+ ### Return a Sequel::Dataset for synsets related to the receiver via the semantic
250
+ ### link of the specified +type+.
251
+ def semanticlink_dataset( type )
252
+ typekey = SEMANTIC_TYPEKEYS[ type ]
253
+ linkinfo = self.class.linktypes[ typekey ] or
254
+ raise ArgumentError, "no such link type %p" % [ typekey ]
255
+ ssids = self.semlinks_dataset.filter( :linkid => linkinfo[:id] ).select( :synset2id )
494
256
 
495
- ### Auto-generate synset pointer methods for the various types
496
-
497
- # The synsets for the receiver's antonyms (opposites). E.g.,
498
- # $lexicon.lookup_synsets( "opaque", :adjective, 1 ).antonyms
499
- # ==> [#<WordNet::Synset:0x010a9acc/454927 clear (adjective): "free
500
- # from cloudiness; allowing light to pass through; "clear water";
501
- # "clear plastic bags"; "clear glass"; "the air is clear and
502
- # clean"" (similar_tos: 6, attributes: 1, derivations: 2,
503
- # antonyms: 1, see_alsos: 1)>]
504
- def_pointer_methods :antonyms
505
-
506
- # Synsets for the receiver's entailments (a verb X entails Y if X cannot
507
- # be done unless Y is or has been done). E.g.,
508
- # $lexicon.lookup_synsets( 'rasp', :verb, 1 ).entailment
509
- # ==> [#<WordNet::Synset:0x010dc24c rub (verb): "move over something
510
- # with pressure; "rub my hands"; "rub oil into her skin""
511
- # (derivations: 2, entailments: 1, hypernyms: 1, hyponyms: 13,
512
- # see_alsos: 4)>]
513
- def_pointer_methods :entailment
514
-
515
- # Get/set synsets for the receiver's cause pointers (a verb X causes Y
516
- # to happen).
517
- def_pointer_methods :causes
518
-
519
- # Get/set synsets for the receiver's verb groups. Verb groups link verbs
520
- # with similar senses together.
521
- def_pointer_methods :verb_groups
522
-
523
- # Get/set list of synsets for the receiver's "similar to" pointers. This
524
- # type of pointer links together head adjective synsets with its
525
- # satellite adjective synsets.
526
- def_pointer_methods :similar_to
527
-
528
- # Get/set synsets for the receiver's participles. Participles are
529
- # non-finite forms of a verb; used adjectivally and to form compound
530
- # tenses. For example, the first participle for "working" is:
531
- # "function, work, operate, go, run (verb)"
532
- def_pointer_methods :participles
533
-
534
- # Get/set synsets for the receiver's pertainyms. Pertainyms are
535
- # relational adjectives. Adjectives that are pertainyms are usually
536
- # defined by such phrases as "of or pertaining to" and do not have
537
- # antonyms. A pertainym can point to a noun or another pertainym.
538
- def_pointer_methods :pertainyms
539
-
540
- # Get/set synsets for the receiver's attributes.
541
- def_pointer_methods :attributes
542
-
543
- # Get/set synsets for the receiver's derived_from.
544
- def_pointer_methods :derived_from
545
-
546
- # Get/set synsets for the receiver's derivations.
547
- def_pointer_methods :derivations
548
-
549
- # Get/set synsets for the receiver's see_also.
550
- def_pointer_methods :see_also
551
-
552
-
553
- # Auto-generate types with subtypes
554
-
555
- # Synsets for the receiver's hypernyms (more-general terms). E.g.,
556
- # $lexicon.lookup_synsets( "cudgel", :noun, 1 ).hypernyms
557
- # ==> [#<WordNet::Synset:0x0109a644/3023321 club (noun): "stout
558
- # stick that is larger at one end; "he carried a club in self
559
- # defense"; "he felt as if he had been hit with a club""
560
- # (derivations: 1, hypernyms: 1, hyponyms: 7)>]
561
- #
562
- # Also generates accessors for subtypes:
563
- #
564
- # [instance_hypernyms]
565
- # A proper noun that refers to a particular, unique referent (as
566
- # distinguished from nouns that refer to classes).
567
- def_pointer_methods :hypernyms
568
-
569
-
570
- # :TODO: Generate an example for this
571
-
572
- # Get/set synsets for the receiver's hyponyms (more-specific terms). E.g.,
573
- # $lexicon.lookup_synsets( "cudgel", :noun, 1 ).hyponyms
574
- # ==> [...]
575
- # [instance_hyponyms]
576
- # The specific term used to designate a member of a class. X is a
577
- # hyponym of Y if X is a (kind of) Y.
578
- # Also generates accessors for subtypes:
579
- #
580
- # [instance_hyponyms]
581
- # A proper noun that refers to a particular, unique referent (as
582
- # distinguished from nouns that refer to classes).
583
- def_pointer_methods :hyponyms
584
-
585
-
586
- # Get/set synsets for the receiver's meronyms. In addition to the
587
- # general accessors for all meronyms, there are also accessors for
588
- # subtypes as well:
589
- #
590
- # [member_meronyms]
591
- # Get/set synsets for the receiver's "member" meronyms (HAS MEMBER
592
- # relation).
593
- # [stuff_meronyms]
594
- # Get/set synsets for the receiver's "stuff" meronyms (IS MADE OUT OF
595
- # relation).
596
- # [portion_meronyms]
597
- # Get/set synsets for the receiver's "portion" meronyms (HAS PORTION
598
- # relation).
599
- # [component_meronyms]
600
- # Get/set synsets for the receiver's "component" meronyms (HAS
601
- # COMPONENT relation).
602
- # [feature_meronyms]
603
- # Get/set synsets for the receiver's "feature" meronyms (HAS FEATURE
604
- # relation).
605
- # [phase_meronyms]
606
- # Get/set synsets for the receiver's "phase" meronyms (HAS PHASE
607
- # relation).
608
- # [place_meronyms]
609
- # Get/set synsets for the receiver's "place" meronyms (HAS PLACE
610
- # relation).
611
- def_pointer_methods :meronyms
612
-
613
- # Get/set synsets for the receiver's holonyms. In addition to the
614
- # general accessors for all holonyms, there are also accessors for
615
- # subtypes as well:
616
- #
617
- # [member_holonyms]
618
- # Get/set synsets for the receiver's "member" holonyms (IS A MEMBER OF
619
- # relation).
620
- # [stuff_holonyms]
621
- # Get/set synsets for the receiver's "stuff" holonyms (IS MATERIAL OF
622
- # relation).
623
- # [portion_holonyms]
624
- # Get/set synsets for the receiver's "portion" holonyms (IS A PORTION
625
- # OF relation).
626
- # [component_holonyms]
627
- # Get/set synsets for the receiver's "component" holonyms (IS A
628
- # COMPONENT OF relation).
629
- # [feature_holonyms]
630
- # Get/set synsets for the receiver's "feature" holonyms (IS A FEATURE
631
- # OF relation).
632
- # [phase_holonyms]
633
- # Get/set synsets for the receiver's "phase" holonyms (IS A PHASE OF
634
- # relation).
635
- # [place_holonyms]
636
- # Get/set synsets for the receiver's "place" holonyms (IS A PLACE IN
637
- # relation).
638
- def_pointer_methods :holonyms
639
-
640
- # Get/set synsets for the receiver's topical domain members. In addition
641
- # to the general members accessor, there are also accessors for
642
- # membership subtypes:
643
- #
644
- # [category_members]
645
- # Get/set synsets for the receiver's
646
- # "category" topical domain members.
647
- # [region_members]
648
- # Get/set synsets for the receiver's "region"
649
- # topical domain members.
650
- # [usage_members]
651
- # Get/set synsets for the receiver's "usage"
652
- # topical domain members.
653
- def_pointer_methods :members
654
-
655
- # Get/set synsets for the receiver's topical domain domains. In addition
656
- # to the general domains accessor, there are also accessors for
657
- # domainship subtypes:
658
- #
659
- # [category_domains]
660
- # Get/set synsets for the receiver's
661
- # "category" topical domain domains.
662
- # [region_domains]
663
- # Get/set synsets for the receiver's "region"
664
- # topical domain domains.
665
- # [usage_domains]
666
- # Get/set synsets for the receiver's "usage"
667
- # topical domain domains.
668
- def_pointer_methods :domains
669
-
670
-
671
- ### Returns an Array of the coordinate sisters of the receiver.
672
- def coordinates
673
- self.hypernyms.collect {|syn|
674
- syn.hyponyms
675
- }.flatten
676
- end
257
+ return self.class.filter( :synsetid => ssids )
258
+ end
677
259
 
678
260
 
679
- ### Return the name of the "lexicographer's file" associated with this
680
- ### synset.
681
- def lex_info
682
- @mutex.synchronize( Sync::SH ) {
683
- return LEXFILES[ self.filenum.to_i ]
684
- }
685
- end
686
-
687
-
688
- ### Sets the "lexicographer's file" association for this synset to
689
- ### +id+. The value in +id+ should correspond to one of the values in
690
- ### #WordNet::LEXFILES
691
- def lexInfo=( id )
692
- raise ArgumentError, "Bad index: Lexinfo id must be within LEXFILES" unless
693
- LEXFILES[id]
694
- @mutex.synchronize( Sync::EX ) {
695
- self.filenum = id
696
- }
697
- end
698
-
699
-
700
- ### Returns an +Array+ of verb frame +String+s for the synset.
701
- def frames
702
- frarray = self.frameslist.split( WordNet::SUB_DELIM_RE )
703
- verbFrames = []
704
-
705
- @mutex.synchronize( Sync::SH ) {
706
- frarray.each {|fr|
707
- fnum, wnum = fr.split
708
- if wnum > 0
709
- wordtext = " (" + self.words[wnum] + ")"
710
- verbFrames.push VERB_SENTS[ fnum ] + wordtext
711
- else
712
- verbFrames.push VERB_SENTS[ fnum ]
713
- end
714
- }
715
- }
716
-
717
- return verbFrames
718
- end
719
-
720
-
721
- ### Traversal iterator: Iterates depth-first over a particular
722
- ### +type+ of the receiver, and all of the pointed-to synset's
723
- ### pointers. If called with a block, the block is called once for each
724
- ### synset with the +foundSyn+ and its +depth+ in relation to the
725
- ### originating synset as arguments. The first call will be the
726
- ### originating synset with a depth of +0+ unless +includeOrigin+ is
727
- ### +false+. If the +callback+ returns +true+, the traversal is halted,
728
- ### and the method returns immediately. This method returns an Array of
729
- ### the synsets which were traversed if no block is given, or a flag
730
- ### which indicates whether or not the traversal was interrupted if a
731
- ### block is given.
732
- def traverse( type, includeOrigin=true )
733
- raise ArgumentError, "Illegal parameter 1: Must be either a String or a Symbol" unless
734
- type.kind_of?( String ) || type.kind_of?( Symbol )
735
-
736
- raise ArgumentError, "Synset doesn't support the #{type.to_s} pointer type." unless
737
- self.respond_to?( type )
738
-
739
- foundSyns = []
740
- depth = 0
741
- traversalFunc = nil
742
-
743
- # Build a traversal function which we can call recursively. It'll return
744
- # the synsets it traverses.
745
- traversalFunc = Proc.new {|syn,newDepth|
746
-
747
- # Flag to continue traversal
748
- haltFlag = false
749
-
750
- # Call the block if it exists and we're either past the origin or
751
- # including it
752
- if block_given? && (newDepth > 0 || includeOrigin)
753
- res = yield( syn, newDepth )
754
- haltFlag = true if res.is_a? TrueClass
755
- end
756
-
757
- # Make an array for holding sub-synsets we see
758
- subSyns = []
759
- subSyns.push( syn ) unless newDepth == 0 && !includeOrigin
760
-
761
- # Iterate over each synset returned by calling the pointer on the
762
- # current syn. For each one, we call ourselves recursively, and
763
- # break out of the iterator with a false value if the block has
764
- # indicated we should abort by returning a false value.
765
- unless haltFlag
766
- syn.send( type ).each {|subSyn|
767
- subSubSyns, haltFlag = traversalFunc.call( subSyn, newDepth + 1 )
768
- subSyns.push( *subSubSyns ) unless subSubSyns.empty?
769
- break if haltFlag
770
- }
771
- end
772
-
773
- # return
774
- [ subSyns, haltFlag ]
775
- }
776
-
777
- # Call the iterator
778
- traversedSets, haltFlag = traversalFunc.call( self, depth )
779
-
780
- # If a block was given, just return whether or not the block was halted.
781
- if block_given?
782
- return haltFlag
783
-
784
- # If no block was given, return the traversed synsets
785
- else
786
- return traversedSets
787
- end
788
- end
789
-
790
-
791
- ### Returns the distance in pointers between the receiver and +otherSynset+
792
- ### using +type+ as the search path.
793
- def distance( type, otherSynset )
794
- dist = nil
795
- self.traverse( type ) {|syn,depth|
796
- if syn == otherSynset
797
- dist = depth
798
- true
799
- end
800
- }
801
-
802
- return dist
803
- end
261
+ ### Return an Enumerator that will iterate over the Synsets related to the receiver
262
+ ### via the semantic links of the specified +linktype+.
263
+ def semanticlink_enum( linktype )
264
+ return self.semanticlink_dataset( linktype ).to_enum
265
+ end
804
266
 
805
267
 
806
- ### Recursively searches all of the receiver's pointers of the specified
807
- ### +type+ for +otherSynset+, returning +true+ if it is found.
808
- def search( type, otherSynset )
809
- self.traverse( type ) {|syn,depth|
810
- syn == otherSynset
811
- }
812
- end
813
-
268
+ ### Return the name of the Synset's part of speech (#pos).
269
+ def part_of_speech
270
+ return self.class.postype_table[ self.pos.to_sym ]
271
+ end
814
272
 
815
- ### Union: Return the least general synset that the receiver and
816
- ### +otherSynset+ have in common as a hypernym, or nil if it doesn't share
817
- ### any.
818
- def |( otherSyn )
819
273
 
820
- # Find all of this syn's hypernyms
821
- hyperSyns = self.traverse( :hypernyms )
822
- commonSyn = nil
274
+ ### Stringify the synset.
275
+ def to_s
823
276
 
824
- # Now traverse the other synset's hypernyms looking for one of our
825
- # own hypernyms.
826
- otherSyn.traverse( :hypernyms ) {|syn,depth|
827
- if hyperSyns.include?( syn )
828
- commonSyn = syn
829
- true
830
- end
831
- }
832
-
833
- return commonSyn
834
- end
277
+ # Make a sorted list of the semantic link types from this synset
278
+ semlink_list = self.semlinks_dataset.
279
+ group_and_count( :linkid ).
280
+ to_hash( :linkid, :count ).
281
+ collect do |linkid, count|
282
+ '%s: %d' % [ self.class.linktype_table[linkid][:typename], count ]
283
+ end.
284
+ sort.
285
+ join( ', ' )
835
286
 
287
+ return "%s (%s): [%s] %s (%s)" % [
288
+ self.words.map( &:to_s ).join(', '),
289
+ self.part_of_speech,
290
+ self.lexical_domain,
291
+ self.definition,
292
+ semlink_list
293
+ ]
294
+ end
836
295
 
837
- ### Returns the pointers in this synset's pointerlist as an +Array+
838
- def pointers
839
- @mutex.synchronize( Sync::SH ) {
840
- @mutex.synchronize( Sync::EX ) {
841
- @pointers = @pointerlist.split(SUB_DELIM_RE).collect {|pstr|
842
- Pointer::parse( pstr )
843
- }
844
- } if @pointers.empty?
845
- @pointers
846
- }
847
- end
848
296
 
297
+ ### Return the name of the lexical domain the synset belongs to; this also
298
+ ### corresponds to the lexicographer's file the synset was originally loaded from.
299
+ def lexical_domain
300
+ return self.class.lexdomain_table[ self.lexdomainid ][ :lexdomainname ]
301
+ end
849
302
 
850
- ### Set the pointers in this synset's pointerlist to +newPointers+
851
- def pointers=( *newPointers )
852
- @mutex.synchronize( Sync::EX ) {
853
- @pointerlist = newPointers.collect {|ptr| ptr.to_s}.join( SUB_DELIM )
854
- @pointers = newPointers
855
- }
856
- end
857
303
 
304
+ ### Return any sample sentences.
305
+ def samples
306
+ return self.db[:samples].
307
+ filter( synsetid: self.synsetid ).
308
+ order( :sampleid ).
309
+ map( :sample ).all
310
+ end
858
311
 
859
- ### Returns the synset's pointers in a Hash keyed by their type.
860
- def pointer_map
861
- return self.pointers.inject( {} ) do |hsh,ptr|
862
- hsh[ ptr.type ] ||= []
863
- hsh[ ptr.type ] << ptr
864
- hsh
865
- end
312
+
313
+ #
314
+ # :section: Semantic Links
315
+ #
316
+
317
+ ##
318
+ # "See Also" synsets
319
+ semantic_link :also_see
320
+
321
+ ##
322
+ # Attribute synsets
323
+ semantic_link :attributes
324
+
325
+ ##
326
+ # Cause synsets
327
+ semantic_link :causes
328
+
329
+ ##
330
+ # Domain category synsets
331
+ semantic_link :domain_categories
332
+
333
+ ##
334
+ # Domain member category synsets
335
+ semantic_link :domain_member_categories
336
+
337
+ ##
338
+ # Domain member region synsets
339
+ semantic_link :domain_member_regions
340
+
341
+ ##
342
+ # Domain member usage synsets
343
+ semantic_link :domain_member_usages
344
+
345
+ ##
346
+ # Domain region synsets
347
+ semantic_link :domain_regions
348
+
349
+ ##
350
+ # Domain usage synsets
351
+ semantic_link :domain_usages
352
+
353
+ ##
354
+ # Verb entailment synsets
355
+ semantic_link :entailments
356
+
357
+ ##
358
+ # Hypernym sunsets
359
+ semantic_link :hypernyms
360
+
361
+ ##
362
+ # Hyponym synsets
363
+ semantic_link :hyponyms
364
+
365
+ ##
366
+ # Instance hypernym synsets
367
+ semantic_link :instance_hypernyms
368
+
369
+ ##
370
+ # Instance hyponym synsets
371
+ semantic_link :instance_hyponyms
372
+
373
+ ##
374
+ # Member holonym synsets
375
+ semantic_link :member_holonyms
376
+
377
+ ##
378
+ # Member meronym synsets
379
+ semantic_link :member_meronyms
380
+
381
+ ##
382
+ # Part holonym synsets
383
+ semantic_link :part_holonyms
384
+
385
+ ##
386
+ # Part meronym synsets
387
+ semantic_link :part_meronyms
388
+
389
+ ##
390
+ # Similar word synsets
391
+ semantic_link :similar_words
392
+
393
+ ##
394
+ # Substance holonym synsets
395
+ semantic_link :substance_holonyms
396
+
397
+ ##
398
+ # Substance meronym synsets
399
+ semantic_link :substance_meronyms
400
+
401
+ ##
402
+ # Verb group synsets
403
+ semantic_link :verb_groups
404
+
405
+
406
+ ### With a block, yield a WordNet::Synset related to the receiver via a link of
407
+ ### the specified +type+, recursing depth first into each of its links if the link
408
+ ### type is recursive. To exit from the traversal at any depth, throw :stop_traversal.
409
+ ###
410
+ ### If no block is given, return an Enumerator that will do the same thing instead.
411
+ ###
412
+ ### # Print all the parts of a boot
413
+ ### puts lexicon[:boot].traverse( :member_meronyms ).all
414
+ ###
415
+ ###
416
+ def traverse( type, &block )
417
+ enum = Enumerator.new do |yielder|
418
+ traversals = [ self.semanticlink_enum(type) ]
419
+ syn = nil
420
+ typekey = SEMANTIC_TYPEKEYS[ type ]
421
+ recurses = self.linktypes[ typekey ][:recurses]
422
+
423
+ self.log.debug "Traversing %s semlinks%s" % [ type, recurses ? " (recursive)" : '' ]
424
+
425
+ catch( :stop_traversal ) do
426
+ until traversals.empty?
427
+ begin
428
+ self.log.debug " %d traversal/s left"
429
+ syn = traversals.last.next
430
+ yielder.yield( syn, traversals.length )
431
+ traversals << syn.semanticlink_enum( type ) if recurses
432
+ rescue StopIteration
433
+ traversals.pop
866
434
  end
435
+ end
436
+ end
437
+ end
867
438
 
439
+ return enum.each( &block ) if block
440
+ return enum
441
+ end
868
442
 
869
443
 
870
- #########
871
- protected
872
- #########
873
-
874
- ### Returns an Array of synset objects for the receiver's pointers of the
875
- ### specified +type+.
876
- def fetch_synset_pointers( type, subtype=nil )
877
- synsets = nil
878
-
879
- # Iterate over this synset's pointers, looking for ones that match
880
- # the type we're after. When we find one, we extract its offset and
881
- # use that to look it up.
882
- @mutex.synchronize( Sync::SH ) do
883
- synsets = self.pointers.
884
- find_all {|ptr|
885
- ptr.type == type and
886
- subtype.nil? || ptr.subtype == subtype
887
- }.
888
- collect {|ptr| ptr.synset }.
889
- collect {|key| @lexicon.lookup_synsets_by_key( key )}
890
- end
891
-
892
- return synsets.flatten
893
- end
894
-
895
-
896
- ### Sets the receiver's synset pointers for the specified +type+ to
897
- ### the specified +synsets+.
898
- def set_synset_pointers( type, synsets, subtype=nil )
899
- synsets = [ synsets ] unless synsets.is_a?( Array )
900
- pmap = self.pointer_map
901
- pmap[ type ] = synsets
902
- self.pointers = pmap.values
903
- end
904
-
444
+ ### Search for the specified +synset+ in the semantic links of the given +type+ of
445
+ ### the receiver, returning the depth it was found at if it's found, or nil if it
446
+ ### wasn't found.
447
+ def search( type, synset )
448
+ found, depth = self.traverse( type ).find {|ss,depth| synset == ss }
449
+ return depth
450
+ end
905
451
 
906
- end # class Synset
907
- end # module WordNet
452
+ end # class WordNet::Synset
908
453