ruby-anagrams 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/anagrams/anagrams.rb +91 -0
- data/lib/anagrams/enumerable.rb +25 -0
- data/lib/anagrams/node.rb +71 -0
- data/lib/anagrams/root.rb +44 -0
- data/lib/anagrams/subtrees.rb +47 -0
- data/lib/ruby-anagrams.rb +5 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -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
|
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:
|