# Documentation: https://github.com/monzita/algorithmix/wiki/Stack module Algorithmix module DataStructure module Generic class Stack # Creates a new stack. # # @param obj [#to_a] any object which responds to #to_a method. # @return [Stack] a new stack object def initialize(obj = nil, copy: false) @container = [] obj.nil? ? nil : from_obj(obj, copy) end # Copies the content of an object to the content of the stack, removing # previous elements inserted in it. # # @param obj [#to_a] any object which responds to to_a method # @return [Stack] self object, modified def assign(obj, copy: false) from_obj(obj, copy) end # Inserts a new element at the top of the stack. # # @param value # @return [Stack] self object, modified def push(value) @container << value self end # (see #push) def <<(value) push(value) end # Removes the top element of the stack. # # @return top element of the stack # @raise Algorithmix::EmptyContainerError, if there are no elements in the stack. def pop raise EmptyContainerError, "The stack is empty." if @container.empty? @container.pop end # Returns the number of elements in the stack. def size @container.size end # (see #size) def length @container.size end # Returns the top element of the stack, without removing it. # If the stack is empty, returns nil. def top @container.last end # Checks if the stack contains any elements. def empty? @container.empty? end # Compares the self stack with given as argument stack. # # @param [Stack] # @return [true, false] def ==(stack) raise ArgumentError, "Undefined method Stack#== for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container == stack.to_a end # (see #==) def eql?(stack) raise ArgumentError, "Undefined method Stack#eql? for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self == stack end # (see #==) def !=(stack) raise ArgumentError, "Undefined method Stack#!= for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container != stack.to_a end # (see #==) def diff?(stack) raise ArgumentError, "Undefined method Stack#diff? for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self != stack end # Compares the self stack with given as argument stck. # # @return 1, if content of the first stack is greater than content of the second. # 0, if both stacks have equal contents # -1, if content of the first stack is less than content of the second. # @raise ArgumentError, if given object is not a stack def <=>(stack) raise ArgumentError, "Undefined method Stack#<=> for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container <=> stack.to_a end # Merges contents of the self stack and stack given as argument, without removing repetitions from both stacks. # # @param [Stack] # @return [Stack] a new stack object, without modifying any of given stacks. # @raise ArgumentError, if given object is not a stack def +(stack) raise ArgumentError, "Undefined method Stack#+ for #{stack}:#{stack.class}" unless stack.is_a?(Stack) Stack.new(@container + stack.to_a) end # (see #+) def merge(stack) raise ArgumentError, "Undefined method Stack#merge for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self + stack end # Merges contents of the self stack, and stack given as argument. # Modifies self object. # # @param [Stack] # @return [Stack] self object # @raise ArgumentError, if given object is not a stack def merge!(stack) raise ArgumentError, "Undefined method Stack#merge! for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container += stack.to_a self end # (see #+) def concat(stack) raise ArgumentError, "Undefined method Stack#concat for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self + stack end # (see #merge!) def concat!(stack) raise ArgumentError, "Undefined method Stack#concat! for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container += stack.to_a self end # Finds intersection of contents of the self stack and stack given as argument, and returns a new stack # with the result. # # @param [Stack] # @return [Stack] a new stack # @raise ArgumentError, if given object is not a stack def &(stack) raise ArgumentError, "Undefined method Stack#& for #{stack}:#{stack.class}" unless stack.is_a?(Stack) Stack.new(@container & stack.to_a) end # (see #&) def intersect(stack) raise ArgumentError, "Undefined method Stack#intersect for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self & stack end # Like intersect, finds intersection of the self stack object and stack given as argument, but modifies the # object to which operation was called. def intersect!(stack) raise ArgumentError, "Undefined method Stack#intersect! for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container &= stack.to_a self end # Finds difference of the self stack object and that given as argument. # # @param [Stack] # @return [Stack] a new stack # @raise ArgumentError, if given object is not a stack def -(stack) raise ArgumentError, "Undefined method Stack#- for #{stack}:#{stack.class}" unless stack.is_a?(Stack) Stack.new(@container - stack.to_a) end # (see #-) def difference(stack) raise ArgumentError, "Undefined method Stack#difference for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self - stack end # As difference, finds the difference of the stack and that given as argument, but # modifies self object. # # @param [Stack] # @return [Stack] self object # @raise ArgumentError, if given object is not a stack def difference!(stack) raise ArgumentError, "Undefined method Stack#difference! for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container -= stack.to_a self end # Finds union of the self object and stack given as argument. # # @param [Stack] # @return [Stack] a new stack # @raise ArgumentError, if given object is not a stack def |(stack) raise ArgumentError, "Undefined method Stack#| for #{stack}:#{stack.class}" unless stack.is_a?(Stack) Stack.new(@container | stack.to_a) end # (see #|) def union(stack) raise ArgumentError, "Undefined method Stack#union for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self | stack end # Like #|, finds the union of the self object and the stack given as argument, but # keeps the result in the current stack. # # @param [Stack] # @return [Stack] self object # @raise ArgumentError, if given object is not a stack def union!(stack) raise ArgumentError, "Undefined method Stack#union! for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container |= stack.to_a self end # Finds the symmetric difference of the stack and that given as argument. # # @param [Stack] # @return [Stack] a new stack # @raise ArgumentError, if given object is not a stack def ^(stack) raise ArgumentError, "Undefined method Stack#^ for #{stack}:#{stack.class}" unless stack.is_a?(Stack) Stack.new((@container | stack.to_a) - (@container & stack.to_a)) end # (see #^) def symmetric_difference(stack) raise ArgumentError, "Undefined method Stack#symmetric_difference for #{stack}:#{stack.class}" unless stack.is_a?(Stack) self ^ stack end # Finds the symmetric difference of the self object and stack given as argument. # # @param [Stack] # @return [Stack] self object, modified # @raise ArgumentError, if given object is not a stack def symmetric_difference!(stack) raise ArgumentError, "Undefined method Stack#symmetric_difference! for #{stack}:#{stack.class}" unless stack.is_a?(Stack) @container = (@container | stack.to_a) - (@container & stack.to_a) self end # Returns current object as array. def to_a @container end # Clears content of current stack. def clear @container = [] self end # Filters elements of the stack, by given condition. # # @param &block represents condition by which elements of the stack will be filtered # @return [Stack] a new stack object def select(&block) Stack.new(@container.select { |e| block.call(e) }) end # Same as #select, but modifying the self object. def select!(&block) @container.select! { |e| block.call(e)} self end # (see #select) def filter(&block) select(&block) end # (see #select!) def filter!(&block) select!(&block) end # (see #select) def find_all(&block) select(&block) end # (see #select!) def find_all!(&block) select!(&block) end # Applies a function to each element of the stack. # # @param &block # @return [Stack] a new stack def map(&block) Stack.new(@container.map { |e| block.call(e)}) end # Same as #map, but the result is kept in the current object. # # @param &block def map!(&block) @container.map! { |e| block.call(e) } self end # (see #map) def apply(&block) map(&block) end # (see #map!) def apply!(&block) map!(&block) end private def from_obj(obj, copy) raise ArgumentError, "Object doesn't respond to #to_a method" unless obj.respond_to?(:to_a) @container = copy ? obj.send(:to_a).dup : obj.send(:to_a) self end end end end end