ruby-anagrams 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b4354f4fdcda04c67e8f3b2e11afbe9b6999fc22
4
+ data.tar.gz: 7cf6680ff7f4de0aa5aff21768852fab28e51d7a
5
+ SHA512:
6
+ metadata.gz: 28216d0fefce0b01b630e56e7eec4d9cf330183c7e76666cd94db7bd17fd8bd8dc5b7a14d7c6121069ec35a70bfce2952ca5aac6780f20b27582e192339b3ada
7
+ data.tar.gz: dceb898b96b5f0b67cdbd014fb1d8bde14586725ed18c35f4f5d6128037c725cf3c3f027067def25a37502b4e26f5be3c131cdbf588c62cf110c0ccb549b01b9
@@ -0,0 +1,91 @@
1
+ module RubyAnagrams
2
+ # Provides anagram solver behavior to the trie data structure.
3
+ # @author Connor Lay
4
+ module Anagrams
5
+ require 'prime'
6
+
7
+ # A Hash associating symbols with unique prime numbers.
8
+ SYM_PRIMES = (:a..:z).to_a.zip(Prime.first(26)).to_h
9
+
10
+ # Returns all words in the trie data structure that are anagrams
11
+ # of the string provided. "*" indicates a wildcard.
12
+ # @note Default behavior finds complete anagrams that use every character
13
+ # in the string. Partial anagrams can be included by setting include_partial
14
+ # to true.
15
+ # @example without partial anagrams
16
+ # root << "rise"
17
+ # root << "sire"
18
+ # root << "rie"
19
+ # #anagrams "rise" #=> ['rise', 'sire']
20
+ # @example with partial anagrams
21
+ # root << "rise"
22
+ # root << "sire"
23
+ # root << "rie"
24
+ # #anagrams "rise", include_partial: true #=> ['rie', 'rise', 'sire']
25
+ # @example with wildcards
26
+ # root << "bin"
27
+ # root << "ban"
28
+ # root << "bun"
29
+ # #anagrams "b*n" #=> ['ban', 'bin', 'bun']
30
+ # @param string [String] the string to find anagrams of.
31
+ # @param include_partial [Boolean] include partial anagrams?
32
+ # @return [Array<String>] all anagrams of the given string.
33
+ def anagrams string, include_partial: false
34
+ symbols = str_to_sym_a string
35
+ anagrams = []
36
+ find_symbol_permutations(symbols).each do |permutation|
37
+ anagrams.concat find_anagrams(as_product(permutation), include_partial: include_partial)
38
+ end
39
+ anagrams.uniq.sort
40
+ end
41
+
42
+ protected
43
+ # Returns anagrams in the trie data structure by performing depth-first search,
44
+ # following subtrees whose symbol's associated prime number is a prime factor
45
+ # of the product representing a string. The words of visited terminal nodes
46
+ # are returned as an array.
47
+ # @note Default behavior finds words that consume all prime factors
48
+ # of the given product. Words that do not consume all prime factors can
49
+ # be included by setting include_partial to true.
50
+ # @param product [Integer] the product representing the string to find
51
+ # anagrams of.
52
+ # @param include_partial [Boolean] include partial anagrams?
53
+ # @return [Array<String>] all anagrams whose product divides into the given
54
+ # product.
55
+ def find_anagrams product, include_partial: false
56
+ anagrams = []
57
+ anagrams << word if terminal? && (include_partial ? true : product == 1)
58
+ @children.each do |symbol,child|
59
+ if product % SYM_PRIMES[symbol] == 0
60
+ anagrams += child.find_anagrams(product / SYM_PRIMES[symbol], include_partial: include_partial)
61
+ end
62
+ end
63
+ anagrams
64
+ end
65
+
66
+ # Returns all permutations of the given set of symbols, where "*" wildcards
67
+ # are replaced with all possible permutations of symbols in the trie's
68
+ # alphabet.
69
+ # @example with 1 wildcard
70
+ # alphabet = [:a, :b, :c]
71
+ # #find_symbol_permutations [:a, :*, :c] #=> [[:a, :a, :c], [:a, :b, :c], [:a, :c, :c]]
72
+ # @param symbols [Array<Symbol>] the symbols to permute.
73
+ # @return [Array<Array<Symbol>>] all permutations of the symbols.
74
+ def find_symbol_permutations symbols
75
+ symbol_permutations = []
76
+ non_wild_symbols = symbols.reject{|s| s == :*}
77
+ (:a..:z).to_a.repeated_permutation(symbols.count :*).each do |permutation|
78
+ symbol_permutations << non_wild_symbols + permutation
79
+ end
80
+ symbol_permutations.empty? ? [symbols] : symbol_permutations
81
+ end
82
+
83
+ # Returns the product of each symbol's associated prime number.
84
+ # @param symbols [Array<Symbol>] the symbols to multiply.
85
+ # @return [Integer] the product of each symbol's associated prime number.
86
+ def as_product symbols
87
+ symbols.inject(1) { |acc,sym| acc * SYM_PRIMES[sym] }
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,25 @@
1
+ module RubyAnagrams
2
+ # Provides enumerable behavior to the trie data structure.
3
+ # @author Connor Lay
4
+ module Enumerable
5
+
6
+ include ::Enumerable
7
+
8
+ # Calls a block for each word in the trie data strucutre. If no block is given,
9
+ # an Enumerator is returned.
10
+ # @return [Enumerator] the enumerator for the words in the trie data structure.
11
+ def each &block
12
+ enumerator = Enumerator.new do |y|
13
+ y << word if terminal?
14
+ @children.each_value do |child|
15
+ child.each { |word| y << word }
16
+ end
17
+ end
18
+ block.nil? ? enumerator : enumerator.each(&block)
19
+ end
20
+
21
+ alias :size :count
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,71 @@
1
+ module RubyAnagrams
2
+ # A representation of a Node in the trie data structure.
3
+ # @author Connor Lay
4
+ class Node
5
+
6
+ include Subtrees
7
+ include Enumerable
8
+ include Anagrams
9
+
10
+ attr_reader :symbol, :children, :parent
11
+
12
+ # Creates a new, non-terminal node with no children.
13
+ # @param symbol [Symbol, nil] the symbol the node represents.
14
+ # @param parent [Node, nil] the parent of the node.
15
+ # @return [Node] the node just created.
16
+ def initialize symbol = nil, parent = nil
17
+ @symbol = symbol
18
+ @parent = parent
19
+ @children = {}
20
+ @terminal = false
21
+ end
22
+
23
+ # Adds a new child node representing a symbol to this node's children.
24
+ # @param symbol [Symbol] the symbol the node represents.
25
+ # @param child [Node] the node to add as a child.
26
+ # @return [Node] the node just added.
27
+ def []= symbol, child
28
+ @children[symbol] = child
29
+ end
30
+
31
+ # Returns the child node associated with a symbol.
32
+ # @param symbol [Symbol]
33
+ # @return [Node] the child node associated with the symbol.
34
+ def [] symbol
35
+ @children[symbol]
36
+ end
37
+
38
+ # Sets the node as a terminal.
39
+ def terminal!
40
+ @terminal = true
41
+ end
42
+
43
+ # Is the node a terminal?
44
+ # @return [Boolean] true if the node is a terminal, false otherwise
45
+ def terminal?
46
+ @terminal
47
+ end
48
+
49
+ # Returns the word the node represents based on its symbol and
50
+ # the symbols of its parents.
51
+ # @example called on the node representing :e
52
+ # :a
53
+ # :p
54
+ # :p
55
+ # :l
56
+ # :e
57
+ # #word #=> "apple"
58
+ # @return [String] the word the node represents.
59
+ def word
60
+ @parent ? "#{@parent.word}#{@symbol}" : "#{@symbol}"
61
+ end
62
+
63
+ def inspect
64
+ "<#{self.class}:#{self.object_id}, "\
65
+ "@symbol=#{@symbol ? ":#{@symbol}" : "nil"}, "\
66
+ "@word=#{terminal? ? word : "nil"}, "\
67
+ "@children=#{@children.each_key.to_a}>"
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,44 @@
1
+ # Namespace for the Ruby-Anagrams gem.
2
+ module RubyAnagrams
3
+ # A representation of the Root node of the trie data structure.
4
+ # @author Connor Lay
5
+ class Root < Node
6
+ # Creates a new trie.
7
+ # @param path [String, nil] the path to a dictionary text file.
8
+ # @return [Root] the Root node of the trie just created.
9
+ def initialize path = nil
10
+ super()
11
+ if path
12
+ file = File.open path, "r"
13
+ file.each_line { |line| self << line.downcase.strip }
14
+ file.close
15
+ end
16
+ end
17
+
18
+ # Adds a word to the trie.
19
+ # @param word [String] the new word to add to the trie.
20
+ # @return [String] the word just added to the trie.
21
+ def << word
22
+ symbols = str_to_sym_a word
23
+ add_to_subtree symbols
24
+ end
25
+
26
+ # If the trie contains the word.
27
+ # @param word [String] the word to search for.
28
+ # @return [Boolean] true if the word is found, false otherwise.
29
+ def include? word
30
+ symbols = str_to_sym_a word
31
+ search_subtree symbols
32
+ end
33
+
34
+ # Returns a symbol array based on the chatacters of a string.
35
+ # @example
36
+ # str_to_sym_a "apple" #=> [:a, :p, :p, :l, :e]
37
+ # @param string [String] the string to process
38
+ # @return [Array<Symbol>] an array of symbols
39
+ def str_to_sym_a string
40
+ string.chars.map { |char| char.to_sym }
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ module RubyAnagrams
2
+ # Provides subtree branching and searching behavior to the trie data strucutre.
3
+ # @author Connor Lay
4
+ module Subtrees
5
+
6
+ # Returns the number of nodes in the subtree. When called on Root, returns
7
+ # the number of nodes in the entire trie data structure.
8
+ # @return [Integer] the number of nodes in the subtree.
9
+ def node_count
10
+ nodes = 1
11
+ @children.each_value do |child|
12
+ nodes += child.node_count
13
+ end
14
+ nodes
15
+ end
16
+
17
+ protected
18
+ # Descends the trie data structure, adding nodes when needed, for a given
19
+ # sequence of symbols. The last node visited is designated as a terminal.
20
+ # @param symbols [Array<Symbol>] the symbol sequence to follow.
21
+ # @return [String] the word represented by the last node visited.
22
+ def add_to_subtree symbols
23
+ if symbols.empty?
24
+ terminal!
25
+ return word
26
+ end
27
+ s = symbols.slice! 0
28
+ unless child = @children[s]
29
+ child = Node.new s, self
30
+ @children[s] = child
31
+ end
32
+ child.add_to_subtree symbols
33
+ end
34
+
35
+ # Performs depth-first search, following a sequence of symbols, on the trie
36
+ # data structure. Returns true if the last node visited is a terminal.
37
+ # @param symbols [Array<Symbol>] the symbol sequence to follow.
38
+ # @return [Boolean] true if the last node visited is a terminal, false otherwise.
39
+ def search_subtree symbols
40
+ return true if symbols.empty? && terminal?
41
+ s = symbols.slice! 0
42
+ return false unless child = @children[s]
43
+ child.search_subtree symbols
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'anagrams/subtrees'
2
+ require_relative 'anagrams/enumerable'
3
+ require_relative 'anagrams/anagrams'
4
+ require_relative 'anagrams/node'
5
+ require_relative 'anagrams/root'
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-anagrams
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Connor Lay
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-its
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A trie-based anagram solver.
70
+ email: connor.lay@me.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/anagrams/anagrams.rb
76
+ - lib/anagrams/enumerable.rb
77
+ - lib/anagrams/node.rb
78
+ - lib/anagrams/root.rb
79
+ - lib/anagrams/subtrees.rb
80
+ - lib/ruby-anagrams.rb
81
+ homepage: https://github.com/connorlay/ruby-anagrams
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.4.6
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Ruby Anagrams
105
+ test_files: []
106
+ has_rdoc: