wallace 0.0.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.
Files changed (99) hide show
  1. data/Gemfile +14 -0
  2. data/README.md +12 -0
  3. data/Rakefile +52 -0
  4. data/VERSION +1 -0
  5. data/bin/.gitkeep +0 -0
  6. data/lib/analysers/fitness_distribution_analyser.rb +28 -0
  7. data/lib/analysers/init.rb +1 -0
  8. data/lib/core/analyser.rb +63 -0
  9. data/lib/core/breeder.rb +68 -0
  10. data/lib/core/breeding_graph.rb +59 -0
  11. data/lib/core/breeding_graph/init.rb +3 -0
  12. data/lib/core/breeding_graph/input_node.rb +55 -0
  13. data/lib/core/breeding_graph/node.rb +68 -0
  14. data/lib/core/breeding_graph/node_input.rb +6 -0
  15. data/lib/core/evaluator.rb +52 -0
  16. data/lib/core/evolver.rb +47 -0
  17. data/lib/core/exceptions.rb +6 -0
  18. data/lib/core/experiment.rb +22 -0
  19. data/lib/core/fitness.rb +8 -0
  20. data/lib/core/fraction.rb +7 -0
  21. data/lib/core/individual.rb +65 -0
  22. data/lib/core/logger.rb +5 -0
  23. data/lib/core/migrator.rb +6 -0
  24. data/lib/core/operator.rb +54 -0
  25. data/lib/core/population.rb +56 -0
  26. data/lib/core/selector.rb +43 -0
  27. data/lib/core/species.rb +52 -0
  28. data/lib/core/state.rb +29 -0
  29. data/lib/core/subpopulation.rb +53 -0
  30. data/lib/core/termination.rb +39 -0
  31. data/lib/distributions/gaussian_distribution.rb +60 -0
  32. data/lib/distributions/init.rb +3 -0
  33. data/lib/fitness/init.rb +1 -0
  34. data/lib/fitness/raw_fitness.rb +30 -0
  35. data/lib/loggers/csv_logger.rb +20 -0
  36. data/lib/loggers/init.rb +1 -0
  37. data/lib/loggers/mongo_logger.rb +5 -0
  38. data/lib/loggers/sqlite_logger.rb +5 -0
  39. data/lib/modules/ge/backus_naur_form.rb +125 -0
  40. data/lib/modules/ge/grammar_derivation.rb +30 -0
  41. data/lib/modules/ge/grammar_species.rb +29 -0
  42. data/lib/modules/ge/init.rb +5 -0
  43. data/lib/modules/init.rb +2 -0
  44. data/lib/modules/koza/builder.rb +48 -0
  45. data/lib/modules/koza/builder/full_builder.rb +92 -0
  46. data/lib/modules/koza/builder/grow_builder.rb +103 -0
  47. data/lib/modules/koza/builder/half_builder.rb +70 -0
  48. data/lib/modules/koza/builder/init.rb +3 -0
  49. data/lib/modules/koza/ephemeral.rb +33 -0
  50. data/lib/modules/koza/init.rb +12 -0
  51. data/lib/modules/koza/koza_node.rb +108 -0
  52. data/lib/modules/koza/koza_node_value.rb +72 -0
  53. data/lib/modules/koza/koza_node_value_set.rb +51 -0
  54. data/lib/modules/koza/koza_species.rb +48 -0
  55. data/lib/modules/koza/koza_tree.rb +159 -0
  56. data/lib/modules/koza/operators/init.rb +4 -0
  57. data/lib/modules/koza/operators/subtree_crossover_operation.rb +43 -0
  58. data/lib/modules/koza/operators/subtree_mutation_operation.rb +32 -0
  59. data/lib/operators/bit_flip_mutation_operation.rb +29 -0
  60. data/lib/operators/boundary_mutation_operation.rb +28 -0
  61. data/lib/operators/cycle_crossover_operation.rb +77 -0
  62. data/lib/operators/gaussian_mutation_operation.rb +39 -0
  63. data/lib/operators/half_uniform_crossover_operation.rb +24 -0
  64. data/lib/operators/init.rb +26 -0
  65. data/lib/operators/merging_crossover_operation.rb +27 -0
  66. data/lib/operators/one_point_crossover_operation.rb +29 -0
  67. data/lib/operators/order_crossover_operation.rb +38 -0
  68. data/lib/operators/partially_mapped_crossover_operation.rb +44 -0
  69. data/lib/operators/point_mutation_operation.rb +31 -0
  70. data/lib/operators/position_crossover_operation.rb +50 -0
  71. data/lib/operators/reverse_sequence_mutation_operation.rb +13 -0
  72. data/lib/operators/shuffle_mutation_operation.rb +17 -0
  73. data/lib/operators/splice_crossover_operation.rb +42 -0
  74. data/lib/operators/subtour_exchange_crossover_operation.rb +54 -0
  75. data/lib/operators/swap_mutation_operation.rb +29 -0
  76. data/lib/operators/three_parent_crossover_operation.rb +16 -0
  77. data/lib/operators/two_point_crossover_operation.rb +31 -0
  78. data/lib/operators/twors_mutation_operation.rb +18 -0
  79. data/lib/operators/uniform_crossover_operation.rb +30 -0
  80. data/lib/operators/uniform_mutation_operation.rb +31 -0
  81. data/lib/operators/variable_one_point_crossover_operation.rb +80 -0
  82. data/lib/patches/enumerable.rb +85 -0
  83. data/lib/patches/init.rb +5 -0
  84. data/lib/patches/range.rb +13 -0
  85. data/lib/selectors/init.rb +5 -0
  86. data/lib/selectors/random_selector.rb +14 -0
  87. data/lib/selectors/roulette_selector.rb +23 -0
  88. data/lib/selectors/tournament_selector.rb +36 -0
  89. data/lib/species/array_species.rb +40 -0
  90. data/lib/species/bit_string_species.rb +18 -0
  91. data/lib/species/init.rb +4 -0
  92. data/lib/species/permutation_species.rb +22 -0
  93. data/lib/species/string_species.rb +29 -0
  94. data/lib/utility/init.rb +4 -0
  95. data/lib/utility/scaled_array.rb +88 -0
  96. data/lib/utility/sorted_array.rb +39 -0
  97. data/lib/wallace.rb +40 -0
  98. data/test/.gitkeep +0 -0
  99. metadata +248 -0
@@ -0,0 +1,92 @@
1
+ # The Full builder constructs Koza trees according to a slight variant of Koza's
2
+ # FULL method. Unlike Koza's original FULL method, this FULL method allows single
3
+ # node trees to be constructed.
4
+ #
5
+ # A random integer d is selected as the depth of the tree, between the minimum
6
+ # and maximum depth limits inclusively. A full tree of exactly depth d is
7
+ # then generated.
8
+ class Wallace::Koza::Builder::FullBuilder < Wallace::Koza::Builder
9
+
10
+ # Builds a full subtree of a given depth using a variant
11
+ # of Koza's FULL method (where single element sub-trees are acceptable).
12
+ #
13
+ # *Parameters:*
14
+ # * terminals, the list of terminals.
15
+ # * non_terminals, the list of non-terminals.
16
+ # * depth, the depth of the sub-tree.
17
+ # * opts, a hash of keyword options for this method.
18
+ # -> random, the RNG to use for building the subtree.
19
+ #
20
+ # *Returns:*
21
+ # The root node of the built sub-tree.
22
+ def self.full_subtree(terminals, non_terminals, depth, opts = {})
23
+
24
+ opts[:random] ||= Random.new
25
+
26
+ # If we require a sub-tree of depth one then we simply return a node with a value
27
+ # sampled from the set of terminals.
28
+ return Wallace::Koza::KozaNode.new(terminals.sample(random: opts[:random])) if depth == 0
29
+
30
+ # Construct the children of each node in the queue until there are no nodes
31
+ # left to process. We assume that the node to be processed has children. This
32
+ # is fine to assume since sub-trees of length one are created and returned above, and
33
+ # terminal nodes aren't added to the queue for processing.
34
+ root = Wallace::Koza::KozaNode.new(non_terminals.sample(random: opts[:random]), depth: 0)
35
+ queue = [root]
36
+ until queue.empty?
37
+
38
+ parent = queue.shift
39
+ parent.children = Array.new(parent.arity) do
40
+
41
+ # To achieve a full tree we simply sample from the non-terminal set until the child
42
+ # is at the maximum depth.
43
+ if (parent.depth + 1) == depth
44
+ value = terminals.sample(random: opts[:random])
45
+ else
46
+ value = non_terminals.sample(random: opts[:random])
47
+ end
48
+
49
+ # Build the node and add it to the queue (provided it isn't terminal).
50
+ node = Wallace::Koza::KozaNode.new(value, parent: parent, depth: parent.depth + 1)
51
+ queue << node unless node.terminal?
52
+ node
53
+
54
+ end unless parent.terminal?
55
+
56
+ end
57
+ return root
58
+
59
+ end
60
+
61
+ # Builds a full tree whose depth is between the minimum and maximum depths
62
+ # inclusively and every sub-tree is full.
63
+ #
64
+ # *Parameters:*
65
+ # * opts, a hash of keyword options for this method.
66
+ # -> random, the RNG to use for building the tree.
67
+ #
68
+ # *Returns:*
69
+ # The built tree.
70
+ def build_tree(opts = {})
71
+ opts[:random] ||= Random.new
72
+ depth = opts[:random].rand(@depth_limits)
73
+ Wallace::Koza::KozaTree.new(build_subtree(depth, random: opts[:random]))
74
+ end
75
+
76
+ # Builds a full subtree of a given maximum depth.
77
+ #
78
+ # *Parameters:*
79
+ # * depth, the maximum depth of the sub-tree.
80
+ # * opts, a hash of keyword options for this method.
81
+ # -> random, the RNG to use for building the subtree.
82
+ #
83
+ # *Returns:*
84
+ # The root node of the built sub-tree.
85
+ #
86
+ # *See*
87
+ # FullBuilder.full_subtree
88
+ def build_subtree(depth, opts = {})
89
+ self.class.full_subtree(@terminals, @non_terminals, depth, opts)
90
+ end
91
+
92
+ end
@@ -0,0 +1,103 @@
1
+ # The Grow builder constructs Koza trees according to a slight variant of Koza's
2
+ # GROW method. Unlike Koza's original GROW method, this GROW method allows single
3
+ # node trees to be constructed.
4
+ #
5
+ # A random integer d is selected as the depth of the tree, between the minimum
6
+ # and maximum depth limits inclusively. A tree of up to depth d is then generated.
7
+ # At each stage we determine whether a node should be terminal or not by checking if
8
+ # the maximum depth has been reached, or some random number falls within the bounds of
9
+ # probability that a terminal should be selected.
10
+ class Wallace::Koza::Builder::GrowBuilder < Wallace::Koza::Builder
11
+
12
+ # The probability that a terminal will be selected when constructing a node using the GROW method.
13
+ attr_accessor :probability_terminal
14
+
15
+ # Builds a full subtree of a given maximum depth using a variant
16
+ # of Koza's GROW method (where single element sub-trees are acceptable).
17
+ #
18
+ # *Parameters:*
19
+ # * probability_terminal, the probability that a terminal node will be created at each stage.
20
+ # * opts, a hash of keyword options for this method.
21
+ # -> random, the RNG to use for building the subtree.
22
+ #
23
+ # *Returns:*
24
+ # The root node of the built sub-tree.
25
+ def self.grow_subtree(terminals, non_terminals, max_depth, probability_terminal, opts = {})
26
+
27
+ opts[:random] ||= Random.new
28
+
29
+ # If the maximum depth of the sub-tree is zero then we simply return a node with a value
30
+ # sampled from the set of terminals. If the maximum depth of the sub-tree is greater than one
31
+ # then we check if the first node is a terminal and if so, we also return a single terminal
32
+ # node.
33
+ if max_depth == 0 or opts[:random].rand <= probability_terminal
34
+ return Wallace::Koza::KozaNode.new(terminals.sample(random: opts[:random]))
35
+ end
36
+
37
+ # Construct the children of each node in the queue until there are no nodes
38
+ # left to process. We decide whether each child is terminal or non-terminal according to the
39
+ # associated probability of a terminal being selected. If the maximum depth has been reached then
40
+ # we force the child to be a terminal.
41
+ root = Wallace::Koza::KozaNode.new(non_terminals.sample(random: opts[:random]), depth: 0)
42
+ queue = [root]
43
+ until queue.empty?
44
+
45
+ parent = queue.shift
46
+ parent.children = Array.new(parent.arity) do
47
+
48
+ if opts[:random].rand <= probability_terminal or (parent.depth + 1) == max_depth
49
+ value = terminals.sample(random: opts[:random])
50
+ else
51
+ value = non_terminals.sample(random: opts[:random])
52
+ end
53
+
54
+ node = Wallace::Koza::KozaNode.new(value, parent: parent, depth: parent.depth + 1)
55
+ queue << node unless node.terminal?
56
+ node
57
+
58
+ end unless parent.terminal?
59
+
60
+ end
61
+ return root
62
+
63
+ end
64
+
65
+ # Constructs a new GrowBuilder.
66
+ #
67
+ # *Parameters:*
68
+ # * probability_terminal, the probability that a terminal node will be built when using the GROW method.
69
+ def initialize(probability_terminal)
70
+ @probability_terminal = probability_terminal
71
+ end
72
+
73
+ # Selects a random integer d, where d is between the minimum and maximum tree depth inclusively, and creates
74
+ # a tree of up to depth d.
75
+ #
76
+ # *Parameters:*
77
+ # * opts, a hash of keyword options for this method.
78
+ # -> random, the RNG to use for building the tree.
79
+ #
80
+ # *Returns:*
81
+ # The built tree.
82
+ def build_tree(opts = {})
83
+ opts[:random] ||= Random.new
84
+ Wallace::Koza::KozaTree.new(build_subtree(opts[:random].rand(@depth_limits), random: opts[:random]))
85
+ end
86
+
87
+ # Builds a full subtree of a given maximum depth.
88
+ #
89
+ # *Parameters:*
90
+ # * max_depth, the maximum depth of the sub-tree.
91
+ # * opts, a hash of keyword options for this method.
92
+ # -> random, the RNG to use for building the subtree.
93
+ #
94
+ # *Returns:*
95
+ # The root node of the built sub-tree.
96
+ #
97
+ # *See*
98
+ # FullBuilder.full_subtree
99
+ def build_subtree(max_depth, opts = {})
100
+ self.class.grow_subtree(@terminals, @non_terminals, max_depth, @probability_terminal, opts)
101
+ end
102
+
103
+ end
@@ -0,0 +1,70 @@
1
+ # The Half Builder is a (slightly modified) implementation of Koza's Ramped Half-and-Half builder.
2
+ #
3
+ # It works by selecting a random integer d, between the minimum and maximum depth inclusively.
4
+ # probability_grow of the time the grow method is used to generate a tree of up to depth d using the
5
+ # GROW builder. (1-probability_grow) of the time the FULL method is used to generate trees of exactly
6
+ # depth d.
7
+ class Wallace::Koza::Builder::HalfBuilder < Wallace::Koza::Builder
8
+
9
+ # The probability that the GROW method will be used to generate a tree.
10
+ attr_reader :probability_grow
11
+
12
+ # The probability that a terminal will be selected when constructing a node using the GROW method.
13
+ attr_accessor :probability_terminal
14
+
15
+ # Constructs a new HalfBuilder.
16
+ #
17
+ # *Parameters:*
18
+ # * probability_grow, the probability that the GROW method will be used when generating a tree.
19
+ # * probability_terminal, the probability that a terminal node will be built when using the GROW method.
20
+ def initialize(probability_grow, probability_terminal)
21
+ @probability_grow = probability_grow
22
+ @probability_terminal = probability_terminal
23
+ end
24
+
25
+ # Returns the probability that the FULL method will be used to generate a tree.
26
+ def probability_full
27
+ 1.0 - @probability_grow
28
+ end
29
+
30
+ # Builds a new Koza Tree of depth d, where d is within the allowed minimum and maximum
31
+ # tree depths, using either the GROW or FULL method.
32
+ #
33
+ # *Parameters:*
34
+ # * opts, a hash of keyword options for this method.
35
+ # -> random, the RNG to use for the process.
36
+ #
37
+ # *Returns:*
38
+ # A new Koza tree.
39
+ def build_tree(opts = {})
40
+ Wallace::Koza::KozaTree.new(build_subtree(@depth_limits.sample, random: opts[:random]))
41
+ end
42
+
43
+ # Builds a new subtree of a maximum given depth using either the GROW or FULL method.
44
+ #
45
+ # *Parameters:*
46
+ # * depth, the (maximum) depth of the subtree.
47
+ # * opts, a hash of keyword options for this method.
48
+ # -> random, the RNG to use for the process.
49
+ def build_subtree(depth, opts = {})
50
+
51
+ opts[:random] ||= Random.new
52
+
53
+ return Wallace::Koza::Builder::GrowBuilder.grow_subtree(
54
+ @terminals,
55
+ @non_terminals,
56
+ depth,
57
+ @probability_terminal,
58
+ random: opts[:random]
59
+ ) if @probability_grow <= opts[:random].rand
60
+
61
+ return Wallace::Koza::Builder::FullBuilder.full_subtree(
62
+ @terminals,
63
+ @non_terminals,
64
+ depth,
65
+ random: opts[:random]
66
+ )
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'full_builder.rb'
2
+ require_relative 'grow_builder.rb'
3
+ require_relative 'half_builder.rb'
@@ -0,0 +1,33 @@
1
+ # An ephemeral instance is a special type of terminal symbol which only assumes its value (from a set
2
+ # of possible values) once it is selected. The Ephemeral class is used to store the set of possible values
3
+ # that a given ephemeral instance can take.
4
+ #
5
+ # It is possible to bias the selection of values by the Ephemeral by using a weighted collection or a distribution.
6
+ #
7
+ # *Parameters:*
8
+ # * values, the range, set or collection of values instances of the ephemeral can assume.
9
+ class Wallace::Koza::Ephemeral
10
+
11
+ # Constructs a new Ephemeral.
12
+ #
13
+ # *Parameters:*
14
+ # * values, the collection of values that instances of this ephemeral can take.
15
+ def initialize(values)
16
+ @values = values
17
+ end
18
+
19
+ # Samples a possible value for this ephemeral.
20
+ #
21
+ # *Parameters:*
22
+ # * opts, a hash of keyword parameters to this method.
23
+ # -> random, the RNG to use when sampling a value.
24
+ def sample(opts = {})
25
+ Wallace::Koza::EphemeralInstance.new(@values.sample)
26
+ end
27
+
28
+ end
29
+
30
+ # Holds the value of an ephemeral.
31
+ #
32
+ # Unlike standard terminal and non-terminal node values, ephemeral values can be manipulated during breeding.
33
+ class Wallace::Koza::EphemeralInstance < Wallace::Koza::Literal; end
@@ -0,0 +1,12 @@
1
+ module Wallace::Koza; end
2
+
3
+ require_relative 'koza_node.rb'
4
+ require_relative 'koza_node_value.rb'
5
+ require_relative 'koza_node_value_set.rb'
6
+ require_relative 'ephemeral.rb'
7
+ require_relative 'builder.rb'
8
+ require_relative 'koza_tree.rb'
9
+ require_relative 'koza_species.rb'
10
+
11
+ require_relative 'builder/init.rb'
12
+ require_relative 'operators/init.rb'
@@ -0,0 +1,108 @@
1
+ # This class is used to represent nodes within a Koza tree.
2
+ class Wallace::Koza::KozaNode
3
+
4
+ # We allow all attributes to be manipulated from outside but
5
+ # trust that all modification to trees is performed through the
6
+ # KozaTree interfaces (which fix depths and calculate the list
7
+ # of nodes in the tree).
8
+ attr_accessor :contents,
9
+ :depth,
10
+ :parent,
11
+ :children
12
+
13
+ # Constructs a new node for a Koza tree.
14
+ #
15
+ # *Parameters:*
16
+ # * contents, the contents of this node.
17
+ # * opts, a hash of keyword options for this tree node.
18
+ # -> parent, the parent of this node.
19
+ # -> children, an array of the children of this node.
20
+ def initialize(contents, opts = {})
21
+ @contents = contents
22
+ @parent = opts[:parent]
23
+ @children = opts[:children] || []
24
+ @depth = opts[:depth]
25
+ end
26
+
27
+ # Sets the parent node of this node.
28
+ #
29
+ # *Parameters:*
30
+ # * node, the node to set as the parent of this node.
31
+ def parent=(node)
32
+ @parent = node
33
+ end
34
+
35
+ # Checks to see if this node is a root node (i.e. it has no parent).
36
+ def root?
37
+ @parent.nil?
38
+ end
39
+
40
+ # Checks to see if this node is a leaf (i.e. it has no children).
41
+ #
42
+ # *Returns*
43
+ # true if leaf, false if not.
44
+ def leaf?
45
+ @children.empty?
46
+ end
47
+
48
+ # Checks whether this node contains a terminal value.
49
+ def terminal?
50
+ @contents.is_a? Wallace::Koza::Terminal
51
+ end
52
+
53
+ # Checks whether the contents of this node are an ephemeral instance.
54
+ #
55
+ # *Returns:*
56
+ # True if the contents are an ephemeral instance, false if otherwise.
57
+ def ephemeral?
58
+ @contents.is_a? Wallace::Koza::EphemeralInstance
59
+ end
60
+
61
+ # Returns the label at this node.
62
+ def label
63
+ @contents.label
64
+ end
65
+
66
+ # Returns the value of this node (or the label in the case of non-terminals).
67
+ def value
68
+ terminal? ? @contents.value : @contents.label
69
+ end
70
+
71
+ # Returns the arity (number of inputs) to this node.
72
+ def arity
73
+ terminal? ? 0 : @contents.arity
74
+ end
75
+
76
+ # Creates a clone of this node (and its children).
77
+ # Parent node is discarded in the clone (unless specified not to do so).
78
+ #
79
+ # *Parameters:*
80
+ # * opts, a hash of keyword options for this method.
81
+ # -> keep_parent, if true maintains depth and parent information.
82
+ # otherwise this information is lost.
83
+ #
84
+ # *Returns:*
85
+ # A clone of this node (and its children).
86
+ def clone(opts = {})
87
+ cloned = Wallace::Koza::KozaNode.new(
88
+ @contents,
89
+ parent: opts[:keep_parent] ? @parent : nil ,
90
+ children: @children.map { |c| c.clone })
91
+ cloned.children.each { |c| c.parent = cloned }
92
+ return cloned
93
+ end
94
+
95
+ # Evaluates the S-expression given by the sub-tree at this node.
96
+ #
97
+ # *Parameters:*
98
+ # * args, the arguments to the function / program (as a hash).
99
+ def eval(args)
100
+ terminal? ? @contents.eval(args) : @contents.eval(@children.map { |c| c.eval(args) })
101
+ end
102
+
103
+ # Converts the sub-tree at this node into a Ruby program string.
104
+ def to_ruby
105
+ terminal? ? @contents.value.to_s : "#{@contents.label}(#{@children.map{ |c| c.to_ruby }.join(',')})"
106
+ end
107
+
108
+ end
@@ -0,0 +1,72 @@
1
+ # Used to represent an option from either the terminal or non-terminal set.
2
+ class Wallace::Koza::KozaNodeValue; end
3
+
4
+ # Holds an option from the terminal set.
5
+ class Wallace::Koza::Terminal < Wallace::Koza::KozaNodeValue
6
+
7
+ attr_reader :value
8
+
9
+ # Constructs a terminal.
10
+ #
11
+ # *Parameters:*
12
+ # * value, the value of the node.
13
+ def initialize(value)
14
+ @value = value
15
+ end
16
+
17
+ end
18
+
19
+ # Holds a variable option from the terminal set.
20
+ class Wallace::Koza::Variable < Wallace::Koza::Terminal
21
+
22
+ # Evaluates this terminal.
23
+ #
24
+ # *Parameters:*
25
+ # * args, the arguments to the program.
26
+ def eval(args)
27
+ args[@value.to_sym]
28
+ end
29
+
30
+ end
31
+
32
+ # Holds a literal option from the terminal set.
33
+ class Wallace::Koza::Literal < Wallace::Koza::Terminal
34
+
35
+ # Evaluates this terminal.
36
+ #
37
+ # *Parameters:*
38
+ # * args, the arguments to the program.
39
+ def eval(args)
40
+ @value
41
+ end
42
+
43
+ end
44
+
45
+ # Holds an option from the non-terminal set.
46
+ class Wallace::Koza::NonTerminal < Wallace::Koza::KozaNodeValue
47
+
48
+ attr_reader :arity,
49
+ :label,
50
+ :function
51
+
52
+ # Constructs a non-terminal.
53
+ #
54
+ # *Parameters:*
55
+ # * label, the label of the non-terminal.
56
+ # * arity, the number of inputs to the non-terminal.
57
+ # * function, the function object for this non-terminal.
58
+ def initialize(label, arity, function)
59
+ @label = label
60
+ @arity = arity
61
+ @function = function
62
+ end
63
+
64
+ # Evaluates this non-terminal.
65
+ #
66
+ # *Parameters:*
67
+ # * inputs, the input values to this non-terminal function.
68
+ def eval(inputs)
69
+ @function.call(*inputs)
70
+ end
71
+
72
+ end