├── structures ├── comparable.rb ├── iterator.rb ├── visitor.rb ├── order_list.rb ├── stack.rb ├── cursor.rb ├── queue.rb ├── module.rb ├── matrix.rb ├── abstract_object.rb ├── deque.rb ├── priority_queue.rb ├── queue_as_linked_list.rb ├── searchable.rb ├── deque_as_linked_list.rb ├── dense_matrix.rb ├── container.rb ├── binary_heap.rb ├── deque_as_array.rb ├── array.rb ├── queue_as_array.rb ├── multi_dimensional_array.rb ├── double_linked_list.rb ├── stack_as_linked_list.rb ├── hash_table.rb ├── binary_tree.rb ├── stack_as_array.rb ├── b_tree.rb ├── binary_search_tree.rb ├── ordered_list_as_linked_list.rb ├── ordered_list_as_array.rb ├── singly_linked_list.rb ├── m_way_tree.rb ├── axioms.rb └── tree.rb ├── patterns ├── creational │ ├── singleton.rb │ ├── factory.rb │ ├── prototype.rb │ ├── builder.rb │ └── abstract_factory.rb ├── structural │ ├── adapter.rb │ ├── facade.rb │ ├── composite.rb │ ├── decorator.rb │ ├── flyweight.rb │ └── proxy.rb └── behavioral │ ├── strategy.rb │ ├── template_method.rb │ ├── momento.rb │ ├── observer.rb │ ├── iterator.rb │ ├── visitor.rb │ ├── command.rb │ ├── interpreter.rb │ ├── mediator.rb │ ├── state.rb │ └── chain_of_responsibility.rb ├── threads ├── gil.rb ├── fibers.rb ├── mutex.rb └── rails.rb ├── algorithms ├── sort │ ├── quick.rb │ ├── insertion.rb │ ├── shell.rb │ ├── merge.rb │ ├── selection.rb │ ├── heap.rb │ └── bubble.rb └── search │ ├── binary.rb │ └── khuth-morris-pratt.rb ├── LICENSE ├── .gitignore ├── solid ├── dependency_inversion.rb ├── liskov_substitution.rb ├── open_close.rb ├── interface_segregation.rb └── single_responsibility.rb └── README.md /structures/comparable.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /structures/iterator.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Iterator < AbstractObject 9 | 10 | abstractmethod :more? 11 | 12 | abstractmethod :succ 13 | 14 | end -------------------------------------------------------------------------------- /structures/visitor.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Visitor < AbstractObject 9 | 10 | abstractmethod :visit 11 | 12 | def done? 13 | false 14 | end 15 | 16 | end -------------------------------------------------------------------------------- /structures/order_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class OrderedList < SearchableContainer 9 | 10 | def initialize 11 | super 12 | end 13 | 14 | abstractmethod :[] 15 | 16 | abstractmethod :findPosition 17 | 18 | end -------------------------------------------------------------------------------- /structures/stack.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Stack < Container 9 | 10 | def initialize 11 | super 12 | end 13 | 14 | abstractmethod :push 15 | 16 | abstractmethod :pop 17 | 18 | abstractmethod :top 19 | 20 | end -------------------------------------------------------------------------------- /structures/cursor.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Cursor < AbstractObject 9 | 10 | abstractmethod :datum 11 | 12 | abstractmethod :insertAfter 13 | 14 | abstractmethod :insertBefore 15 | 16 | abstractmethod :withdraw 17 | 18 | end -------------------------------------------------------------------------------- /structures/queue.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Queue < Container 9 | 10 | def initialize 11 | super 12 | end 13 | 14 | abstractmethod :enqueue 15 | 16 | abstractmethod :dequeue 17 | 18 | abstractmethod :head 19 | 20 | end -------------------------------------------------------------------------------- /structures/module.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Module 9 | 10 | def abstractmethod(symbol) 11 | module_eval <<-"end_eval" 12 | def #{symbol.id2name}(*args) 13 | raise MethodNotImplementedError 14 | end 15 | end_eval 16 | end 17 | 18 | end -------------------------------------------------------------------------------- /patterns/creational/singleton.rb: -------------------------------------------------------------------------------- 1 | # The singleton pattern is used to ensure that there is only one instance of a 2 | # class and provides global access to that instance. This pattern is useful when 3 | # you want one instance of a class and many different objects need to access it, 4 | # rather than pass the object around, we can make the object global. 5 | 6 | class Singleton 7 | def self.new 8 | @instance ||= super 9 | end 10 | end 11 | 12 | # Other example 13 | require 'singleton' 14 | 15 | class OtherSingleton 16 | include Singleton 17 | 18 | # OtherSingleton.new is not allowed 19 | end 20 | 21 | # Usage 22 | s1 = Singleton.new 23 | s2 = OtherSingleton.instance 24 | 25 | -------------------------------------------------------------------------------- /threads/gil.rb: -------------------------------------------------------------------------------- 1 | # This simple fact is what makes threads so powerful, and also what makes them 2 | # difficult to work with. I've already given you an idea of why threads are 3 | # good; here's a simple program to illustrate their difficulty. 4 | 5 | shared_array = Array.new 6 | 7 | 10.times.map do 8 | Thread.new do 9 | 1000.times do 10 | shared_array << nil 11 | end 12 | end 13 | end.each(&:join) 14 | 15 | # Here you can see that we have 10 * 10000 elements in array 16 | 17 | puts shared_array.size 18 | 19 | # Note that different ruby can show different result 20 | # GIL exist only in MRI ruby 21 | 22 | # $ ruby => 10000 23 | # $ jruby => 7521 24 | # $ rbx => 8541 25 | 26 | -------------------------------------------------------------------------------- /algorithms/sort/quick.rb: -------------------------------------------------------------------------------- 1 | # Shell sort: Similar approach as insertion sort but slightly better. 2 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should 3 | # be implemented for the container. 4 | # Time Complexity: О(n^2) 5 | # Space Complexity: О(n) total, O(1) auxiliary 6 | # Stable: Yes 7 | # 8 | # Algorithms::Sort.shell_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5] 9 | 10 | class Array 11 | def swap(first, second) 12 | self[first], self[second] = self[second], self[first] 13 | self 14 | end 15 | 16 | def quick_sort 17 | h, *t = self 18 | h ? t.partition { |e| e < h }.inject { |l, r| l.quick_sort + [h] + r.quick_sort } : [] 19 | end 20 | end -------------------------------------------------------------------------------- /structures/matrix.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Matrix 9 | 10 | def initialize(numberOfRows, numberOfColumns) 11 | assert { numberOfRows >= 0 } 12 | assert { numberOfColumns >= 0 } 13 | 14 | @numberOfRows = numberOfRows 15 | @numberOfColumns = numberOfColumns 16 | end 17 | 18 | attr_reader :numberOfRows 19 | 20 | attr_reader :numberOfColumns 21 | 22 | abstractmethod :+ 23 | 24 | abstractmethod :* 25 | 26 | abstractmethod :transpose 27 | 28 | end -------------------------------------------------------------------------------- /structures/abstract_object.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | class AbstractObject < ::Object 8 | 9 | def initialize 10 | super 11 | end 12 | 13 | include ::Comparable 14 | 15 | def <=>(obj) 16 | if is_a?(obj.type) 17 | return compareTo(obj) 18 | elsif obj.is_a?(type) 19 | return -obj.compareTo(self) 20 | else 21 | return type.name <=> obj.type.name 22 | end 23 | end 24 | 25 | abstractmethod :compareTo 26 | 27 | protected :compareTo 28 | 29 | end -------------------------------------------------------------------------------- /structures/deque.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | module DequeMethods 9 | 10 | abstractmethod :enqueueHead 11 | 12 | abstractmethod :dequeueHead 13 | 14 | abstractmethod :head 15 | 16 | abstractmethod :enqueueTail 17 | 18 | abstractmethod :dequeueTail 19 | 20 | abstractmethod :tail 21 | 22 | end 23 | 24 | class Deque < Queue 25 | 26 | def initialize 27 | super 28 | end 29 | 30 | alias_method :queueHead, :head 31 | include DequeMethods 32 | alias_method :head, :queueHead 33 | 34 | end -------------------------------------------------------------------------------- /algorithms/sort/insertion.rb: -------------------------------------------------------------------------------- 1 | # Insertion sort: Elements are inserted sequentially into the right position. 2 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should 3 | # be implemented for the container. 4 | # Time Complexity: О(n^2) 5 | # Space Complexity: О(n) total, O(1) auxiliary 6 | # Stable: Yes 7 | # 8 | # Algorithms::Sort.insertion_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5] 9 | 10 | class Array 11 | def insertion_sort! 12 | return self if length < 2 13 | 14 | 1.upto(length - 1) do |i| 15 | value = self[i] 16 | j = i - 1 17 | while j >= 0 and self[j] > value do 18 | self[j + 1] = self[j] 19 | j = j - 1 20 | end 21 | self[j + 1] = value 22 | end 23 | 24 | self 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /algorithms/sort/shell.rb: -------------------------------------------------------------------------------- 1 | # Shell sort: Similar approach as insertion sort but slightly better. 2 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should 3 | # be implemented for the container. 4 | # Time Complexity: О(n^2) 5 | # Space Complexity: О(n) total, O(1) auxiliary 6 | # Stable: Yes 7 | # 8 | # Algorithms::Sort.shell_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5] 9 | 10 | class Array 11 | def shell_sort! 12 | inc = length / 2 13 | 14 | while inc != 0 15 | inc.step(length-1) do |i| 16 | el = self[i] 17 | while i >= inc and self[i - inc] > el 18 | self[i] = self[i - inc] 19 | i -= inc 20 | end 21 | self[i] = el 22 | end 23 | inc = (inc == 2 ? 1 : (inc * 5.0 / 11).to_i) 24 | end 25 | 26 | self 27 | end 28 | end -------------------------------------------------------------------------------- /patterns/structural/adapter.rb: -------------------------------------------------------------------------------- 1 | # An adapter helps two incompatible interfaces to work together. This is the 2 | # real world definition for an adapter. Interfaces may be incompatible but the 3 | # inner functionality should suit the need. The Adapter design pattern allows 4 | # otherwise incompatible classes to work together by converting the interface 5 | # of one class into an interface expected by the clients. 6 | 7 | class Adaptee 8 | def walk 9 | 'walk_as_adaptee' 10 | end 11 | end 12 | 13 | class Adaptor 14 | attr_reader :adaptee 15 | 16 | def initialize 17 | @adaptee = Adaptee.new 18 | end 19 | 20 | def walk 21 | adaptee.walk + ' and_sing_song_as_adaptor' 22 | end 23 | end 24 | 25 | # Usage 26 | 27 | adaptor = Adaptor.new 28 | adaptor.walk # => walk_as_adaptor and_sing_song_as_adaptor 29 | 30 | -------------------------------------------------------------------------------- /structures/priority_queue.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | module PriorityQueueMethods 9 | 10 | abstractmethod :enqueue 11 | 12 | abstractmethod :min 13 | 14 | abstractmethod :dequeueMin 15 | 16 | end 17 | 18 | class PriorityQueue < Container 19 | 20 | def initialize 21 | super 22 | end 23 | 24 | include PriorityQueueMethods 25 | 26 | end 27 | 28 | module MergeablePriorityQueueMethods 29 | 30 | abstractmethod :merge! 31 | end 32 | 33 | class MergeablePriorityQueue < PriorityQueue 34 | 35 | def initialize 36 | super 37 | end 38 | 39 | include MergeablePriorityQueueMethods 40 | 41 | end -------------------------------------------------------------------------------- /structures/queue_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class QueueAsLinkedList < Queue 9 | 10 | def initialize 11 | super 12 | @list = SinglyLinkedList.new 13 | end 14 | 15 | def purge 16 | @list.purge 17 | super 18 | end 19 | 20 | def head 21 | raise ContainerEmpty if @count == 0 22 | @list.first 23 | end 24 | 25 | def enqueue(obj) 26 | @list.append(obj) 27 | @count += 1 28 | end 29 | 30 | def dequeue 31 | raise ContainerEmpty if @count == 0 32 | result = @list.first 33 | @list.extract(result) 34 | @count -= 1 35 | result 36 | end 37 | 38 | end -------------------------------------------------------------------------------- /algorithms/search/binary.rb: -------------------------------------------------------------------------------- 1 | # Binary Search: This search finds an item in log(n) time provided that the container is already sorted. 2 | # The method returns the item if it is found, or nil if it is not. If there are duplicates, the first one 3 | # found is returned, and this is not guaranteed to be the smallest or largest item. 4 | # 5 | # Complexity: O(lg N) 6 | # 7 | # [1, 2, 3].binary_search(1) #=> 1 8 | # [1, 2, 3].binary_search(4) #=> nil 9 | 10 | class Array 11 | def binary_search(item) 12 | return nil if item.nil? 13 | 14 | low = 0 15 | high = self.size - 1 16 | 17 | while low <= high 18 | mid = (low + high) / 2 19 | val = self[mid] 20 | if val > item 21 | high = mid - 1 22 | elsif val < item 23 | low = mid + 1 24 | else 25 | return val 26 | end 27 | end 28 | 29 | nil 30 | end 31 | end -------------------------------------------------------------------------------- /patterns/behavioral/strategy.rb: -------------------------------------------------------------------------------- 1 | # Define a family of algorithms, encapsulate each one, and make them 2 | # interchangeable. Strategy lets the algorithm vary independently from clients that use it. 3 | 4 | # Strategies 5 | class StrategyOne 6 | def use 7 | puts "Strategy one" 8 | end 9 | end 10 | 11 | class StrategyTwo 12 | def use 13 | puts "Strategy two" 14 | end 15 | end 16 | 17 | class StrategyThree 18 | def use 19 | puts "Strategy three" 20 | end 21 | end 22 | 23 | # Client 24 | class Context 25 | @@default_strategy = StrategyOne.new 26 | 27 | def set_strategy(strategy) 28 | @strategy = strategy 29 | end 30 | 31 | def use 32 | @strategy.use 33 | end 34 | end 35 | 36 | # Usage 37 | context = Context.new 38 | context.use # => "Strategy one" 39 | 40 | context.set_strategy StrategyTwo.new 41 | context.use # => "Strategy two" 42 | 43 | -------------------------------------------------------------------------------- /algorithms/sort/merge.rb: -------------------------------------------------------------------------------- 1 | # Mergesort: A stable divide-and-conquer sort that sorts small chunks of the container and then merges them together. 2 | # Returns an array of the sorted elements. 3 | # Requirements: Container should implement [] 4 | # Time Complexity: О(n log n) average and worst-case 5 | # Space Complexity: О(n) auxiliary 6 | # Stable: Yes 7 | # 8 | # Algorithms::Sort.mergesort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5] 9 | 10 | class Array 11 | def merge_sort! 12 | return self if self.size <= 1 13 | mid = self.size / 2 14 | left = self[0...mid] 15 | right = self[mid...self.size] 16 | merge(left.merge_sort!, right.merge_sort!) 17 | end 18 | 19 | def merge(left, right) 20 | sorted = [] 21 | until left.empty? or right.empty? 22 | left.first <= right.first ? sorted << left.shift : sorted << right.shift 23 | end 24 | sorted + left + right 25 | end 26 | end -------------------------------------------------------------------------------- /threads/fibers.rb: -------------------------------------------------------------------------------- 1 | # Fibers are primitives for implementing light weight cooperative concurrency in 2 | # Ruby. Basically they are a means of creating code blocks that can be paused 3 | # and resumed, much like threads. The main difference is that they are never 4 | # preempted and that the scheduling must be done by the programmer and not the VM. 5 | 6 | require 'fiber' 7 | 8 | f = Fiber.new do |value| 9 | first = value 10 | puts "first call fiber with args: #{first ? first : 'not passed'}" 11 | second = Fiber.yield 12 | puts "second call fiber with args: #{second ? second : 'not passed'}" 13 | other = Fiber.yield 14 | puts "First: #{first}, Second: #{second}, Other: #{other ? other : 'not passed'}" 15 | puts "Last call" 16 | end 17 | 18 | f.resume('First argument') 19 | f.resume('Second argument') 20 | f.resume 21 | # f.resume call error if try call one more time fiber is dead 22 | 23 | -------------------------------------------------------------------------------- /structures/searchable.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | module SearchableContainerMethods 9 | 10 | abstractmethod :member? 11 | 12 | abstractmethod :insert 13 | 14 | abstractmethod :withdraw 15 | 16 | abstractmethod :find 17 | 18 | end 19 | 20 | class SearchableContainer < Container 21 | 22 | include SearchableContainerMethods 23 | 24 | def initialize 25 | super 26 | end 27 | 28 | end 29 | 30 | module SearchTreeMethods 31 | 32 | abstractmethod :min 33 | 34 | abstractmethod :max 35 | 36 | end 37 | 38 | class SearchTree < Tree 39 | 40 | def initialize 41 | super 42 | end 43 | 44 | include SearchableContainerMethods 45 | include SearchTreeMethods 46 | 47 | end -------------------------------------------------------------------------------- /patterns/creational/factory.rb: -------------------------------------------------------------------------------- 1 | # Factory pattern is one of most used design pattern in Object oriented design. 2 | # This type of design pattern comes under creational pattern as this pattern 3 | # provides one of the best ways to create an object. In Factory pattern, we 4 | # create object without exposing the creation logic to the client and refer to 5 | # newly created object using a common interface. 6 | 7 | # Classes witch are used in factory 8 | class Rectangle 9 | # Implementation 10 | end 11 | 12 | class Square 13 | # Implementation 14 | end 15 | 16 | class Circle 17 | # Implementation 18 | end 19 | 20 | # Factory 21 | class ShapeFactory 22 | def get_shape(type) 23 | case type 24 | when :rectangle then Rectangle.new 25 | when :square then Square.new 26 | when :circle then Circle.new 27 | end 28 | end 29 | end 30 | 31 | # Usage 32 | 33 | shape_factory = ShapeFactory.new 34 | square = shape_factory.get_shape :square 35 | 36 | -------------------------------------------------------------------------------- /structures/deque_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DequeAsLinkedList < QueueAsLinkedList 9 | 10 | alias_method :queueHead, :head 11 | 12 | include DequeMethods 13 | 14 | def initialize 15 | super 16 | end 17 | 18 | alias_method :head, :queueHead 19 | 20 | def enqueueHead(obj) 21 | @list.prepend(obj) 22 | @count += 1 23 | end 24 | 25 | alias_method :dequeueHead, :dequeue 26 | 27 | def tail 28 | raise ContainerEmpty if @count == 0 29 | @list.last 30 | end 31 | 32 | alias_method :enqueueTail, :enqueue 33 | 34 | def dequeueTail 35 | raise ContainerEmpty if @count == 0 36 | result = @list.last 37 | @list.extract(result) 38 | @count -= 1 39 | result 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /patterns/behavioral/template_method.rb: -------------------------------------------------------------------------------- 1 | # Define the skeleton of an algorithm in an operation, deferring some steps to 2 | # subclasses. Template method lets subclasses redefine certain steps of an 3 | # algorithm without changing the algorithm's structure. 4 | 5 | module Template 6 | #mandatory_methods = ["tagname", "content"] 7 | #optional_methods = ["font_size", "background_color"] 8 | 9 | def generate 10 | string = "<#{tagname}" 11 | styles = '' 12 | styles += "font-size:#{font_size};" if respond_to? :font_size 13 | styles += "background-color:#{background_color};" if respond_to? :background_color 14 | string += " style='#{styles}'" if !styles.empty? 15 | string += ">#{content}" 16 | end 17 | end 18 | 19 | class Body 20 | def tagname 21 | "body" 22 | end 23 | 24 | def content 25 | "hello" 26 | end 27 | 28 | def font_size 29 | "18pt" 30 | end 31 | 32 | include Template 33 | end 34 | 35 | # Usage 36 | body = Body.new 37 | body.generate -------------------------------------------------------------------------------- /structures/dense_matrix.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DenseMatrix < Matrix 9 | 10 | def initialize(rows, cols) 11 | super 12 | @array = MultiDimensionalArray.new(rows, cols) 13 | end 14 | 15 | def [](i, j) 16 | @array[i,j] 17 | end 18 | 19 | def []=(i, j, value) 20 | @array[i,j] = value 21 | end 22 | 23 | def *(mat) 24 | assert { numberOfColumns == mat.numberOfRows } 25 | result = DenseMatrix.new( 26 | numberOfRows, mat.numberOfColumns) 27 | for i in 0 ... numberOfRows 28 | for j in 0 ... mat.numberOfColumns 29 | sum = 0 30 | for k in 0 ... numberOfColumns 31 | sum += self[i,k] * mat[k,j] 32 | end 33 | result[i,j] = sum 34 | end 35 | end 36 | 37 | result 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /structures/container.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Container < AbstractObject 9 | 10 | include ::Enumerable 11 | 12 | def initialize 13 | super 14 | @count = 0 15 | end 16 | 17 | attr_reader :count 18 | 19 | def purge 20 | @count = 0 21 | end 22 | 23 | def empty? 24 | count == 0 25 | end 26 | 27 | def full? 28 | false 29 | end 30 | 31 | abstractmethod :iter 32 | 33 | def each 34 | i = iter 35 | while i.more? 36 | yield i.succ 37 | end 38 | end 39 | 40 | def to_s 41 | s = "" 42 | each do |obj| 43 | s << ", " if not s.empty? 44 | s << obj.to_s 45 | end 46 | type.name + "{" + s + "}" 47 | end 48 | 49 | def accept(visitor) 50 | assert { visitor.is_a?(Visitor) } 51 | each do |obj| 52 | break if visitor.done? 53 | visitor.visit(obj) 54 | end 55 | end 56 | 57 | end 58 | 59 | -------------------------------------------------------------------------------- /patterns/structural/facade.rb: -------------------------------------------------------------------------------- 1 | # The goal of the Facade Pattern is to provide a unified interface to a set of 2 | # interfaces in a subsystem. This means you'd just have some object that can 3 | # send back other objects. 4 | 5 | # Complex Parts 6 | class CPU 7 | def freeze 8 | # Implementation 9 | end 10 | 11 | def jump(position) 12 | # Implementation 13 | end 14 | 15 | def execute 16 | # Implementation 17 | end 18 | end 19 | 20 | class Memory 21 | def load(position, data) 22 | # Implementation 23 | end 24 | end 25 | 26 | class HardDrive 27 | def read(lba, size) 28 | # Implementation 29 | end 30 | end 31 | 32 | # Facade 33 | class ComputerFacade 34 | attr_reader :processor, :ram, :hd 35 | 36 | def initialize 37 | @processor = CPU.new 38 | @ram = Memory.new 39 | @hd = HardDrive.new 40 | end 41 | 42 | def start 43 | processor.freeze 44 | processor.jump(BOOT_ADDRESS) 45 | processor.execute 46 | ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE)) 47 | end 48 | end 49 | 50 | # Usage 51 | computer_facade = ComputerFacade.new 52 | computer_facade.start 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Marat Khusnetdinov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /algorithms/sort/selection.rb: -------------------------------------------------------------------------------- 1 | # Selection sort: A naive sort that goes through the container and selects the smallest element, 2 | # putting it at the beginning. Repeat until the end is reached. 3 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should 4 | # be implemented for the container. 5 | # Time Complexity: О(n^2) 6 | # Space Complexity: О(n) total, O(1) auxiliary 7 | # Stable: Yes 8 | # 9 | # Algorithms::Sort.selection_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5] 10 | 11 | class Array 12 | def swap(first, second) 13 | self[first], self[second] = self[second], self[first] 14 | self 15 | end 16 | 17 | def selection_sort! 18 | 0.upto(length - 1) do |i| 19 | min = i 20 | (i + 1).upto(length - 1) do |j| 21 | min = j if (self[j] <=> self[min]) == -1 22 | end 23 | swap min, i 24 | end 25 | 26 | self 27 | end 28 | 29 | # def selection_sort! 30 | # for i in 0..length - 2 31 | # min = i 32 | # for j in (i + 1)...length 33 | # min = j if self[j] < self[min] 34 | # end 35 | # swap min, i 36 | # end 37 | # 38 | # self 39 | # end 40 | end 41 | -------------------------------------------------------------------------------- /structures/binary_heap.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class BinaryHeap < PriorityQueue 9 | 10 | def enqueue(obj) 11 | raise ContainerFull if @count == @array.length 12 | @count += 1 13 | i = @count 14 | while i > 1 and @array[i/2] > obj 15 | @array[i] = @array[i / 2] 16 | i /= 2 17 | end 18 | @array[i] = obj 19 | end 20 | 21 | def min 22 | raise ContainerEmpty if @count == 0 23 | @array[1] 24 | end 25 | 26 | def dequeueMin 27 | raise ContainerEmpty if @count == 0 28 | result = @array[1] 29 | last = @array[@count] 30 | @count -= 1 31 | i = 1 32 | while 2 * i < @count + 1 33 | child = 2 * i 34 | if child + 1 < @count + 1 and @array[child + 1] < @array[child] 35 | child += 1 36 | end 37 | break if last <= @array[child] 38 | @array[i] = @array[child] 39 | i = child 40 | end 41 | @array[i] = last 42 | return result 43 | end 44 | 45 | end -------------------------------------------------------------------------------- /algorithms/sort/heap.rb: -------------------------------------------------------------------------------- 1 | # Heap sort: Uses a heap (implemented by the Containers module) to sort the collection. 2 | # Requirements: Needs to be able to compare elements with <=> 3 | # Time Complexity: О(n^2) 4 | # Space Complexity: О(n) total, O(1) auxiliary 5 | # Stable: Yes 6 | # 7 | # Algorithms::Sort.heapsort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5] 8 | 9 | class Array 10 | def swap(first, second) 11 | self[first], self[second] = self[second], self[first] 12 | self 13 | end 14 | 15 | def heap_sort! 16 | self.dup.heap_sort 17 | end 18 | 19 | def heap_sort 20 | ((length - 2) / 2).downto(0) {|start| sift_down(start, length - 1)} 21 | 22 | (length - 1).downto(1) do |end_| 23 | swap 0, end_ 24 | sift_down(0, end_ - 1) 25 | end 26 | self 27 | end 28 | 29 | def sift_down(start, end_) 30 | root = start 31 | loop do 32 | child = root * 2 + 1 33 | break if child > end_ 34 | if child + 1 <= end_ and self[child] < self[child + 1] 35 | child += 1 36 | end 37 | if self[root] < self[child] 38 | swap child, root 39 | root = child 40 | else 41 | break 42 | end 43 | end 44 | end 45 | end -------------------------------------------------------------------------------- /patterns/structural/composite.rb: -------------------------------------------------------------------------------- 1 | # In software engineering, the composite pattern is a partitioning design 2 | # pattern. The composite pattern describes that a group of objects is to be 3 | # treated in the same way as a single instance of an object. The intent of a 4 | # composite is to "compose" objects into tree structures to represent part-whole 5 | # hierarchies. Implementing the composite pattern lets clients treat individual 6 | # objects and compositions uniformly 7 | 8 | # Tasks witch are used in composition 9 | class MonsterTask 10 | attr_reader :reward 11 | 12 | def initialize 13 | @reward = 100 14 | end 15 | end 16 | 17 | class PuzzleTask 18 | attr_reader :reward 19 | 20 | def initialize 21 | @reward = 200 22 | end 23 | end 24 | 25 | # Composer 26 | class Quest 27 | def initialize 28 | # Here we use Array as example of data structure for composition. Always is used Tree 29 | @tasks = [] 30 | end 31 | 32 | def <<(task) 33 | @tasks << task 34 | end 35 | 36 | def reward 37 | @tasks.inject(0){ |sum, task| sum += task.reward } 38 | end 39 | end 40 | 41 | # Usage 42 | quest = Quest.new 43 | quest << MonsterTask.new 44 | quest << PuzzleTask.new 45 | puts quest.reward # => 300 46 | -------------------------------------------------------------------------------- /patterns/structural/decorator.rb: -------------------------------------------------------------------------------- 1 | # The decorator design pattern allows for features to be added dynamically to an 2 | # existing object. 3 | 4 | class Decorator 5 | def initialize(item) 6 | @item = item 7 | end 8 | 9 | def use 10 | item.use 11 | end 12 | 13 | # Dynamic method that added in decorator 14 | def another_use 15 | item.use + "another way" 16 | end 17 | end 18 | 19 | class MagicDecorator < Decorator 20 | def description 21 | @item.description + "Magic" 22 | end 23 | end 24 | 25 | class MasterpieceDecorator < Decorator 26 | def description 27 | @item.description + "Masterpiece" 28 | end 29 | end 30 | 31 | # Class what should be decorated 32 | class Item 33 | attr_reader :description 34 | 35 | def initialize 36 | @description = "Item " 37 | end 38 | 39 | def use 40 | "use it" 41 | end 42 | end 43 | 44 | # Usage 45 | item = Item.new 46 | puts item.description # => Item 47 | 48 | magic_item = MagicDecorator.new(item) 49 | puts magic_item.description # => Item Magic 50 | 51 | masterpiece_item = MasterpieceDecorator.new(item) 52 | puts masterpiece_item.description # => Item Masterpiece 53 | 54 | # All next lines puts "use it" 55 | item.use 56 | magic_item.use 57 | masterpiece_item.use -------------------------------------------------------------------------------- /structures/deque_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DequeAsArray < QueueAsArray 9 | 10 | alias_method :queueHead, :head 11 | 12 | include DequeMethods 13 | 14 | def initialize(size = 0) 15 | super(size) 16 | end 17 | 18 | alias_method :head, :queueHead 19 | 20 | def enqueueHead(obj) 21 | raise ContainerFull if @count == @array.length 22 | if @head == 0 23 | @head = @array.length - 1 24 | else 25 | @head = @head - 1 26 | end 27 | @array[@head] = obj 28 | @count += 1 29 | end 30 | 31 | alias_method :dequeueHead, :dequeue 32 | 33 | def tail 34 | raise ContainerEmpty if @count == 0 35 | @array[@tail] 36 | end 37 | 38 | alias_method :enqueueTail, :enqueue 39 | 40 | def dequeueTail 41 | raise ContainerEmpty if @count == 0 42 | result = @array[@tail] 43 | @array[@tail] = nil 44 | if @tail == 0 45 | @tail = @array.length - 1 46 | else 47 | @tail = @tail - 1 48 | end 49 | @count -= 1 50 | result 51 | end 52 | 53 | end -------------------------------------------------------------------------------- /structures/array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Array 9 | 10 | attr_accessor :baseIndex 11 | alias_method :init, :initialize 12 | alias_method :getitem, :[] 13 | alias_method :setitem, :[]= 14 | 15 | def initialize(size = 0, baseIndex = 0) 16 | init(size, nil) 17 | @baseIndex = baseIndex 18 | end 19 | 20 | def length=(value) 21 | tmp = Array.new(value, nil) 22 | 23 | for i in 0 ... [length, value].min 24 | tmp.setitem(i, getitem(i)) 25 | end 26 | 27 | clear 28 | setitem(value - 1, nil) if value > 0 29 | 30 | for i in 0 ... tmp.length 31 | setitem(i, tmp.getitem(i)) 32 | end 33 | end 34 | 35 | protected :getitem, :setitem 36 | 37 | def getOffset(index) 38 | @baseIndex = 0 if @baseIndex.nil? 39 | raise IndexError if not (@baseIndex ... @baseIndex + length) === index 40 | index - @baseIndex 41 | end 42 | 43 | def [](index) 44 | getitem(getOffset(index)) 45 | end 46 | 47 | def []=(index, value) 48 | setitem(getOffset(index), value) 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /patterns/behavioral/momento.rb: -------------------------------------------------------------------------------- 1 | # Without violating encapsulation, capture and externalize an object's internal 2 | # state allowing the object to be restored to this state later. 3 | 4 | # The Originator can save and load itself. The Caretaker (the main function in 5 | # this case) never has to touch the memento objects. 6 | 7 | # This implementation is a bit naive: 8 | # - saves should be kept in files 9 | # - Marshal will not always work (singleton methods, bindings, etc..) 10 | 11 | module Originator 12 | def saves 13 | @saves ||= {} 14 | end 15 | 16 | def save(key) 17 | puts "saving key: #{key}" 18 | @saves[key] = Marshal.dump self 19 | end 20 | 21 | def restore(key) 22 | puts "restoring key: #{key}" 23 | include_state Marshal.load(@saves[key]) 24 | end 25 | 26 | private 27 | 28 | def include_state(other) 29 | other.instance_variables.each { |v| instance_variable_set(v, other.instance_variable_get(v)) unless v == '@saves' } 30 | end 31 | end 32 | 33 | class Example 34 | include Originator 35 | 36 | attr_accessor :name, :color 37 | 38 | def initialize(name, color) 39 | @name, @color = name, color 40 | end 41 | end 42 | 43 | # Usage 44 | example = Example.new('Dave', 'blue') 45 | example.save :now # => saving key now 46 | -------------------------------------------------------------------------------- /structures/queue_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class QueueAsArray < Queue 9 | 10 | def initialize(size = 0) 11 | super() 12 | @array = Array.new(size) 13 | @head = 0 14 | @tail = size - 1 15 | end 16 | 17 | def purge 18 | while @count > 0 19 | @array[@head] = nil 20 | @head = @head + 1 21 | if @head == @array.length 22 | @head = 0 23 | end 24 | @count -= 1 25 | end 26 | end 27 | 28 | def head 29 | raise ContainerEmpty if @count == 0 30 | @array[@head] 31 | end 32 | 33 | def enqueue(obj) 34 | raise ContainerFull if @count == @array.length 35 | @tail = @tail + 1 36 | if @tail == @array.length 37 | @tail = 0 38 | end 39 | @array[@tail] = obj 40 | @count += 1 41 | end 42 | 43 | def dequeue 44 | raise ContainerEmpty if @count == 0 45 | result = @array[@head] 46 | @array[@head] = nil 47 | @head = @head + 1 48 | if @head == @array.length 49 | @head = 0 50 | end 51 | @count -= 1 52 | result 53 | end 54 | 55 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | 13 | # Used by dotenv library to load environment variables. 14 | # .env 15 | 16 | ## Specific to RubyMotion: 17 | .dat* 18 | .repl_history 19 | build/ 20 | *.bridgesupport 21 | build-iPhoneOS/ 22 | build-iPhoneSimulator/ 23 | 24 | ## Specific to RubyMotion (use of CocoaPods): 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 29 | # 30 | # vendor/Pods/ 31 | 32 | ## Documentation cache and generated files: 33 | /.yardoc/ 34 | /_yardoc/ 35 | /doc/ 36 | /rdoc/ 37 | 38 | ## Environment normalization: 39 | /.bundle/ 40 | /vendor/bundle 41 | /lib/bundler/man/ 42 | 43 | # for a library or gem, you might want to ignore these files since the code is 44 | # intended to run in multiple environments; otherwise, check them in: 45 | # Gemfile.lock 46 | # .ruby-version 47 | # .ruby-gemset 48 | 49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 50 | .rvmrc 51 | 52 | # Jetbrains 53 | .idea/* 54 | -------------------------------------------------------------------------------- /structures/multi_dimensional_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class MultiDimensionalArray 9 | 10 | def initialize(*dimensions) 11 | @dimensions = Array.new(dimensions.length) 12 | @factors = Array.new(dimensions.length) 13 | 14 | product = 1 15 | i = dimensions.length - 1 16 | while i >= 0 17 | @dimensions[i] = dimensions[i] 18 | @factors[i] = product 19 | product *= @dimensions[i] 20 | i -= 1 21 | end 22 | 23 | @data = Array.new(product) 24 | end 25 | 26 | def getOffset(indices) 27 | raise IndexError if indices.length != @dimensions.length 28 | 29 | offset = 0 30 | for i in 0 ... @dimensions.length 31 | if indices[i] < 0 or indices[i] >= @dimensions[i] 32 | raise IndexError 33 | end 34 | offset += @factors[i] * indices[i] 35 | end 36 | 37 | offset 38 | end 39 | 40 | def [](*indices) 41 | @data[self.getOffset(indices)] 42 | end 43 | 44 | def []=(*indicesAndValue) 45 | value = indicesAndValue.pop 46 | @data[self.getOffset(indicesAndValue)] = value 47 | end 48 | end -------------------------------------------------------------------------------- /algorithms/sort/bubble.rb: -------------------------------------------------------------------------------- 1 | # Bubble sort: A very naive sort that keeps swapping elements until the container is sorted. 2 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should 3 | # be implemented for the container. 4 | # Time Complexity: О(n^2) 5 | # Space Complexity: О(n) total, O(1) auxiliary 6 | # Stable: Yes 7 | # 8 | # Algorithms::Sort.bubble_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5] 9 | 10 | class Array 11 | def swap(first, second) 12 | self[first], self[second] = self[second], self[first] 13 | self 14 | end 15 | 16 | def bubble_sort! 17 | loop do 18 | swapped = false 19 | (self.size - 1).times do |index| 20 | if self[index] > self[index + 1] 21 | swap index, index + 1 22 | swapped = true 23 | end 24 | end 25 | break unless swapped 26 | end 27 | 28 | self 29 | end 30 | 31 | # def bubble_sort! 32 | # length.times do |j| 33 | # for i in 1...(length - j) 34 | # swap i - 1, i if self[i - 1] < self[i] 35 | # end 36 | # end 37 | # 38 | # self 39 | # end 40 | # 41 | # def bubble_sort! 42 | # each_index do |index| 43 | # (length - 1).downto( index ) do |i| 44 | # swap i - 1, i if self[i - 1] < self[i] 45 | # end 46 | # end 47 | # 48 | # self 49 | # end 50 | end 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /patterns/creational/prototype.rb: -------------------------------------------------------------------------------- 1 | # The prototype pattern is a creational pattern along the lines of the factory. 2 | # The trick with the prototype is that you create new objects by copying a master 3 | # object. Change that master object and all subsequent objects that you create 4 | # will go off into life with a copy of the change. 5 | 6 | # Prototypes witch are used for prototyping 7 | class Note 8 | attr_accessor :duration 9 | 10 | def initialize(duration) 11 | @duration = duration 12 | end 13 | end 14 | 15 | class Clef 16 | # Implementation 17 | end 18 | 19 | 20 | class PrototypeManager 21 | def initialize 22 | @prototypes = [] 23 | end 24 | 25 | def set(key, prototype) 26 | if @prototypes.include? key 27 | raise IndexError, "A prototype is already assigned to this key: #{key}" 28 | else 29 | @prototypes[key] = prototype 30 | end 31 | end 32 | 33 | def unset(key) 34 | if !@prototypes.include? key 35 | raise IndexError, "This key doesn't exist in prototype: #{key}" 36 | else 37 | @prototypes.delete key 38 | end 39 | 40 | end 41 | 42 | def get(key) 43 | @prototypes[key].deep_copy 44 | end 45 | end 46 | 47 | # Usage 48 | 49 | prototype = PrototypeManager.new 50 | prototype.set :half_note, Note.new(0.5) 51 | prototype.set :full_note, Note.new(1) 52 | 53 | note = prototype.get(:full_note) 54 | 55 | -------------------------------------------------------------------------------- /structures/double_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DoubleLinkedList < SinglyLinkedList 9 | class Element < SinglyLinkedList < Element 10 | attr_accessor :prev 11 | 12 | def initialize(list, data, succ, prev) 13 | super(list, data, succ) 14 | @prev = prev 15 | end 16 | 17 | def insert_after(item) 18 | # Todo 19 | end 20 | 21 | def insert_before(item) 22 | # Todo 23 | end 24 | end 25 | 26 | attr_accessor :head, :tail 27 | 28 | def initialize 29 | @head = nil 30 | @tail = nil 31 | end 32 | 33 | def purge 34 | @head = nil 35 | @tail = nil 36 | end 37 | 38 | def empty? 39 | @head.nil? 40 | end 41 | 42 | def first 43 | if @head.nil? 44 | raise Error 45 | else 46 | @head.data 47 | end 48 | end 49 | 50 | def last 51 | if @tail.nil? 52 | raise Error 53 | else 54 | @tail.data 55 | end 56 | end 57 | 58 | def prepend(elem) 59 | # Todo 60 | end 61 | 62 | def append(elem) 63 | # Todo 64 | end 65 | 66 | def clone 67 | # Todo 68 | end 69 | 70 | def extract 71 | # Todo 72 | end 73 | 74 | end -------------------------------------------------------------------------------- /structures/stack_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class StackAsLinkedList < Stack 9 | 10 | def initialize 11 | super 12 | @list = SinglyLinkedList.new 13 | end 14 | 15 | def purge 16 | @list.purge 17 | super 18 | end 19 | 20 | def push(obj) 21 | @list.prepend(obj) 22 | @count += 1 23 | end 24 | 25 | def pop 26 | raise ContainerEmpty if @count == 0 27 | result = @list.first 28 | @list.extract(result) 29 | @count -= 1 30 | return result 31 | end 32 | 33 | def top 34 | raise ContainerEmpty if @count == 0 35 | return @list.first 36 | end 37 | 38 | def each(&block) 39 | @list.each(&block) 40 | end 41 | 42 | attr_reader :list 43 | 44 | class Iterator < Opus8::Iterator 45 | 46 | def initialize(stack) 47 | @position = stack.list.head 48 | end 49 | 50 | def more? 51 | not @position.nil? 52 | end 53 | 54 | def succ 55 | if more? 56 | result = @position.datum 57 | @position = @position.succ 58 | else 59 | result = nil 60 | end 61 | return result 62 | end 63 | end 64 | 65 | def iter 66 | Iterator.new(self) 67 | end 68 | 69 | end -------------------------------------------------------------------------------- /structures/hash_table.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class HashTable < SearchableContainer 9 | 10 | def initialize 11 | super 12 | end 13 | 14 | abstractmethod :length 15 | 16 | def loadFactor 17 | return count / length 18 | end 19 | 20 | def f(obj) 21 | obj.hash 22 | end 23 | 24 | def g(x) 25 | x.abs % length 26 | end 27 | 28 | def h(obj) 29 | g(f(obj)) 30 | end 31 | 32 | end 33 | 34 | class ChainedHashTable < HashTable 35 | 36 | def initialize(length) 37 | super() 38 | @array = Array.new(length) 39 | for i in 0 ... length 40 | @array[i] = SinglyLinkedList.new 41 | end 42 | end 43 | 44 | def length 45 | @array.length 46 | end 47 | 48 | def purge 49 | for i in 0 ... length 50 | @array[i].purge 51 | end 52 | @count = 0 53 | end 54 | 55 | def insert(obj) 56 | @array[h(obj)].append(obj) 57 | @count += 1 58 | end 59 | 60 | def withdraw(obj) 61 | @array[h(obj)].extract(obj) 62 | @count -= 1 63 | end 64 | 65 | def find(obj) 66 | ptr = @array[h(obj)].head 67 | while not ptr.nil? 68 | return ptr.datum if ptr.datum == obj 69 | ptr = ptr.succ 70 | end 71 | nil 72 | end 73 | 74 | end -------------------------------------------------------------------------------- /algorithms/search/khuth-morris-pratt.rb: -------------------------------------------------------------------------------- 1 | # Knuth-Morris-Pratt Algorithm substring search algorithm: Efficiently finds the starting position of a 2 | # substring in a string. The algorithm calculates the best position to resume searching from if a failure 3 | # occurs. 4 | # 5 | # The method returns the index of the starting position in the string where the substring is found. If there 6 | # is no match, nil is returned. 7 | # 8 | # Complexity: O(n + k), where n is the length of the string and k is the length of the substring. 9 | # 10 | # "ABC ABCDAB ABCDABCDABDE".kmp_search("ABCDABD") #=> 15 11 | # "ABC ABCDAB ABCDABCDABDE".kmp_search("ABCDEF") #=> nil 12 | 13 | class Array 14 | def self.kmp_search(substring) 15 | return nil if self.nil? or substring.nil? 16 | 17 | pos = 2 18 | cnd = 0 19 | failure_table = [-1, 0] 20 | while pos < substring.length 21 | if substring[pos - 1] == substring[cnd] 22 | failure_table[pos] = cnd + 1 23 | pos += 1 24 | cnd += 1 25 | elsif cnd > 0 26 | cnd = failure_table[cnd] 27 | else 28 | failure_table[pos] = 0 29 | pos += 1 30 | end 31 | end 32 | 33 | m = i = 0 34 | while m + i < self.length 35 | if substring[i] == self[m + i] 36 | i += 1 37 | return m if i == substring.length 38 | else 39 | m = m + i - failure_table[i] 40 | i = failure_table[i] if i > 0 41 | end 42 | end 43 | 44 | return nil 45 | end 46 | end -------------------------------------------------------------------------------- /patterns/behavioral/observer.rb: -------------------------------------------------------------------------------- 1 | # An object, called the subject, maintains a list of its dependents, called 2 | # observers, and notifies them automatically of any state changes, usually by 3 | # calling one of their methods. 4 | 5 | module Observable 6 | def initialize 7 | @observers = [] 8 | end 9 | 10 | def add_observer(observer) 11 | @observers << observer unless @observers.include?(observer) 12 | end 13 | 14 | def delete_observer(observer) 15 | @observers.delete(observer) 16 | end 17 | 18 | def notify_observers 19 | @observers.each { |x| x.update(self) } 20 | end 21 | end 22 | 23 | class Employee 24 | include Observable 25 | 26 | attr_reader :name 27 | attr_accessor :title, :salary 28 | 29 | def initialize(name, title, salary) 30 | super() 31 | @name, @title, @salary = name, title, salary 32 | end 33 | end 34 | 35 | class BaseObserver 36 | def update 37 | raise 'Must be implement "update" function' 38 | end 39 | end 40 | 41 | class Payroll < BaseObserver 42 | def update(employee) 43 | p("Cut a new check for #{employee.name}!") 44 | p("His salary is now #{employee.salary}!") 45 | end 46 | end 47 | 48 | class TaxMan < BaseObserver 49 | def update(employee) 50 | p("Send #{employee.name} a new tax bill!") 51 | end 52 | end 53 | 54 | # Usage 55 | mike = Employee.new('Mike', 'project manger', 25000) 56 | 57 | mike.add_observer(Payroll.new) 58 | mike.add_observer(TaxMan.new) 59 | 60 | mike.salary = 35000 61 | mike.title = 'senior project manger' 62 | mike.notify_observers 63 | -------------------------------------------------------------------------------- /structures/binary_tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class BinaryTree < Tree 9 | 10 | def initialize(*args) 11 | super() 12 | case args.length 13 | when 0 14 | @key = nil 15 | @left = nil 16 | @right = nil 17 | when 1 18 | @key = args[0] 19 | @left = BinaryTree.new 20 | @right = BinaryTree.new 21 | when 3 22 | @key = args[0] 23 | @left = args[1] 24 | @right = args[2] 25 | else 26 | raise ArgumentError 27 | end 28 | end 29 | 30 | def purge 31 | @key = nil 32 | @left = nil 33 | @right = nil 34 | end 35 | 36 | def left 37 | raise StateError if empty? 38 | @left 39 | end 40 | 41 | def right 42 | raise StateError if empty? 43 | @right 44 | end 45 | 46 | def left 47 | raise StateError if empty? 48 | @left 49 | end 50 | 51 | def right 52 | raise StateError if empty? 53 | @right 54 | end 55 | 56 | def compareTo(bt) 57 | assert { is_a?(obj.type) } 58 | if empty? 59 | if bt.empty? 60 | return 0 61 | else 62 | return -1 63 | end 64 | elsif bt.empty? 65 | return 1 66 | else 67 | result = @key <=> bt._key 68 | result = @left <=> bt._left if result == 0 69 | result = @right <=> bt._right if result == 0 70 | return result 71 | end 72 | end 73 | 74 | end -------------------------------------------------------------------------------- /structures/stack_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class StackAsArray < Stack 9 | 10 | def initialize(size = 0) 11 | super() 12 | @array = Array.new(size) 13 | end 14 | 15 | def purge 16 | while @count > 0 17 | @count -= 1 18 | @array[@count] = nil 19 | end 20 | end 21 | 22 | def push(obj) 23 | raise ContainerFull if @count == @array.length 24 | @array[@count] = obj 25 | @count += 1 26 | end 27 | 28 | def pop 29 | raise ContainerEmpty if @count == 0 30 | @count -= 1 31 | result = @array[@count] 32 | @array[@count] = nil 33 | return result 34 | end 35 | 36 | def top 37 | raise ContainerEmpty if @count == 0 38 | return @array[@count - 1] 39 | end 40 | 41 | def each 42 | for i in 0 ... @count 43 | yield @array[i] 44 | end 45 | end 46 | 47 | attr_reader :array 48 | 49 | class Iterator < Opus8::Iterator 50 | 51 | def initialize(stack) 52 | @stack = stack 53 | @position = 0 54 | end 55 | 56 | def more? 57 | @position < @stack.count 58 | end 59 | 60 | def succ 61 | if more? 62 | assert { more? } 63 | result = @stack.array[@position] 64 | @position += 1 65 | else 66 | result = nil 67 | end 68 | return result 69 | end 70 | end 71 | 72 | def iter 73 | Iterator.new(self) 74 | end 75 | 76 | end -------------------------------------------------------------------------------- /solid/dependency_inversion.rb: -------------------------------------------------------------------------------- 1 | # The Dependency Inversion Principle has to do with high-level (think business 2 | # logic) objects not depending on low-level (think database querying and IO) 3 | # implementation details. This can be achieved with duck typing and the 4 | # Dependency Inversion Principle. Often this pattern is used to achieve the 5 | # Open/Closed Principle that we discussed above. In fact, we can even reuse 6 | # that same example as a demonstration of this principle. 7 | 8 | # Now there is a formatter class, but I've hardcoded it on the Report class, 9 | # thus creating a dependency from the Report to the JSONFormatter. Since the 10 | # Report is a more abstract (high-level) concept than the JSONFormatter, we're 11 | # effectively breaking the DIP. 12 | 13 | class Report 14 | def body 15 | # Implementation 16 | end 17 | 18 | def print 19 | JSONFormatter.new.format(body) 20 | end 21 | end 22 | 23 | class JSONFormatter 24 | def format(body) 25 | # Implementation 26 | end 27 | end 28 | 29 | # Solution 30 | 31 | # This way the Report does not depend on the JSONFormatter and can use any type 32 | # of formatter that has a method called format (this is known as duck typing). 33 | # Another thing of note is that we've used, once again, dependency injection to 34 | # solve a problem. This technique is a very powerful one when our goal is 35 | # decoupling objects, and even though it has the same initials as the dependency 36 | # inversion principle (vs dependency injection pattern), they are completely 37 | # different concepts. 38 | 39 | class Report 40 | def body 41 | # Implementation 42 | end 43 | 44 | def print(formatter: JSONFormatter.new) 45 | formatter.format body 46 | end 47 | end 48 | 49 | -------------------------------------------------------------------------------- /patterns/creational/builder.rb: -------------------------------------------------------------------------------- 1 | # The very simple idea behind the Builder pattern is that you encapsulate object 2 | # construction logic behind a class of its own. The builder class takes charge of 3 | # assembling all the components of a complex object. Each builder has an interface 4 | # that lets you specify the configuration of your new object step by step. In a 5 | # sense, a builder is a sort of like a multipart new method, where objects are 6 | # created in an extended process instead of all in one shot. 7 | 8 | # Classes witch are used in builder 9 | class Computer 10 | # Implementation 11 | end 12 | 13 | class TurboCPU 14 | # Implementation 15 | end 16 | 17 | class Drive 18 | # Implementation 19 | end 20 | 21 | # Builder 22 | class ComputerBuilder 23 | attr_reader :computer 24 | 25 | def initialize 26 | @computer = Computer.new 27 | end 28 | 29 | def turbo(has_turbo_cpu = true) 30 | computer.cpu = TurboCPU.new 31 | end 32 | 33 | def display=(display) 34 | computer.display=display 35 | end 36 | 37 | def memory_size=(size_in_mb) 38 | computer.memory_size = size_in_mb 39 | end 40 | 41 | def add_cd(writer=false) 42 | computer.drives << Drive.new(:cd, 760, writer) 43 | end 44 | 45 | def add_dvd(writer=false) 46 | computer.drives << Drive.new(:dvd, 4000, writer) 47 | end 48 | 49 | def add_hard_disk(size_in_mb) 50 | computer.drives << Drive.new(:hard_disk, size_in_mb, true) 51 | end 52 | end 53 | 54 | # Usage 55 | 56 | computer_builder = ComputerBuilder.new 57 | 58 | computer_builder.turbo false 59 | computer_builder.display = 'Monitor' 60 | computer_builder.add_dvd false 61 | computer_builder.add_cd = false 62 | computer_builder.memory_size = '12mb' 63 | 64 | computer = computer_builder.computer 65 | 66 | -------------------------------------------------------------------------------- /patterns/behavioral/iterator.rb: -------------------------------------------------------------------------------- 1 | # Provide a way to access the elements of an aggregate object sequentially 2 | # without exposing its underlying representation. 3 | 4 | # Iteration entity 5 | class Account 6 | attr_reader :balance 7 | 8 | def initialize(balance) 9 | @balance = balance 10 | end 11 | end 12 | 13 | # Collection class 14 | class Bank 15 | include Enumerable 16 | 17 | def initialize 18 | @accounts = [] 19 | end 20 | 21 | def each(&block) 22 | @accounts.each(&block) 23 | end 24 | 25 | def add(account) 26 | @accounts << account 27 | end 28 | end 29 | 30 | # Usage 31 | bank = Bank.new 32 | bank.add Account.new(100) 33 | bank.add Account.new(150) 34 | bank.add Account.new(175) 35 | 36 | puts bank.map { |account| account.balance } 37 | 38 | # Internal iterator 39 | class Collection 40 | def initialize(array) 41 | @array = array 42 | @index = 0 43 | end 44 | 45 | def has_next? 46 | @index < @array.length 47 | end 48 | 49 | def next_item 50 | value = @array[@index] 51 | @index += 1 52 | value 53 | end 54 | 55 | def iterate(&block) 56 | while has_next? 57 | block.call(next_item) if block_given? 58 | end 59 | end 60 | end 61 | 62 | # Usage 63 | collection = Collection.new([1, 2, 3, 4, 5]) 64 | collection.iterate { |elem| puts elem } 65 | 66 | # External iterator 67 | class Collection 68 | def initialize(array) 69 | @array = array 70 | @index = 0 71 | end 72 | 73 | def has_next? 74 | @index < @array.length 75 | end 76 | 77 | def next_item 78 | value = @array[@index] 79 | @index += 1 80 | value 81 | end 82 | end 83 | 84 | # Usage 85 | collection = Collection.new([1, 2, 3, 4, 5]) 86 | puts "Item: #{collection.next_item}" while collection.has_next? 87 | 88 | -------------------------------------------------------------------------------- /patterns/behavioral/visitor.rb: -------------------------------------------------------------------------------- 1 | # Represent an operation to be performed on the elements of an object structure. 2 | # Visitor lets a new operation be defined without changing the classes of the 3 | # elements on which it operates. 4 | 5 | module Node 6 | def accept visitor 7 | if self.kind_of? StringNode 8 | visitor.visit_string self if visitor.respond_to? :visit_string 9 | elsif self.kind_of? IntegerNode 10 | visitor.visit_int self if visitor.respond_to? :visit_int 11 | end 12 | end 13 | end 14 | 15 | class StringNode 16 | include Node 17 | attr_accessor :string 18 | 19 | def initialize(val) 20 | @string = val 21 | end 22 | end 23 | 24 | class IntegerNode 25 | include Node 26 | attr_accessor :int 27 | 28 | def initialize(val) 29 | @int = val 30 | end 31 | end 32 | 33 | class PrintingVisitor 34 | def visit_string(node) 35 | puts node.string 36 | end 37 | def visit_int(node) 38 | puts node.int 39 | end 40 | end 41 | 42 | class RevertingVisitor 43 | def visit_string(node) 44 | puts node.string.reverse! 45 | end 46 | end 47 | 48 | # Usage 49 | myTreeRoot = Tree::TreeNode.new("ROOT", StringNode.new("this is the root node")) 50 | 51 | myTreeRoot << Tree::TreeNode.new("child1", StringNode.new("madam im adam")) << Tree::TreeNode.new("grandchild1", IntegerNode.new(3)) << Tree::TreeNode.new("grandchild2", IntegerNode.new(2)) 52 | myTreeRoot << Tree::TreeNode.new("child2", StringNode.new("race car")) < ['the light is on', 'the light is off'] 79 | -------------------------------------------------------------------------------- /patterns/creational/abstract_factory.rb: -------------------------------------------------------------------------------- 1 | # Abstract Factory patterns work around a super-factory which creates other 2 | # factories. This factory is also called as factory of factories. This type of 3 | # design pattern comes under creational pattern as this pattern provides one of 4 | # the best ways to create an object. 5 | 6 | # In Abstract Factory pattern an interface is responsible for creating a factory 7 | # of related objects without explicitly specifying their classes. Each generated 8 | # factory can give the objects as per the Factory pattern. 9 | 10 | # Classes witch is used in abstract factories 11 | 12 | class Frog 13 | # Implementation 14 | end 15 | 16 | class Algae 17 | # Implementation 18 | end 19 | 20 | class Tiger 21 | # Implementation 22 | end 23 | 24 | class Tree 25 | # Implementation 26 | end 27 | 28 | # Abstract factory with realization pond environment 29 | class PondFactory 30 | def new_animal 31 | Frog.new 32 | end 33 | 34 | def new_plant 35 | Algae.new 36 | end 37 | end 38 | 39 | # Abstract factory with realization jungle environment 40 | class JungleFactory 41 | def new_animal 42 | Tiger.new 43 | end 44 | 45 | def new_plant 46 | Tree.new 47 | end 48 | end 49 | 50 | # Factory of factories 51 | class EnvironmentFactory 52 | attr_reader :factories 53 | 54 | def initialize 55 | @factories = %w(PondFactory JungleFactory) 56 | end 57 | 58 | def get_factory(method) 59 | factory_class = get_factory_class method 60 | 61 | if factories.include? factory_class 62 | self.class.const_get(factory_class).new 63 | else 64 | super 65 | end 66 | end 67 | 68 | private 69 | 70 | def get_factory_class(method) 71 | "#{method.to_s.capitalize}Factory" 72 | end 73 | end 74 | 75 | # Usage 76 | environment = EnvironmentFactory.new 77 | jungle = environment.get_factory(:jungle) 78 | jungle.new_animal 79 | 80 | -------------------------------------------------------------------------------- /solid/liskov_substitution.rb: -------------------------------------------------------------------------------- 1 | # Liskov’s principle tends to be the most difficult to understand. The principle 2 | # states that you should be able to replace any instances of a parent class 3 | # with an instance of one of its children without creating any unexpected or 4 | # incorrect behaviors. 5 | 6 | class Rectangle 7 | def initialize(height, width) 8 | @height = height 9 | @width = width 10 | end 11 | 12 | def set_height(height) 13 | @height = height 14 | end 15 | 16 | def set_width(width) 17 | @width = width 18 | end 19 | 20 | def square 21 | @width * @height 22 | end 23 | end 24 | 25 | # Solution 26 | 27 | # LSP says is if we know the interface of Rectangle, We need to be able to guess 28 | # the interface of subtype class Square 29 | # Square.new(3).square => 9 30 | 31 | class Square < Rectangle 32 | def initialize(side) 33 | super(side, side) 34 | end 35 | 36 | def set_height(height) 37 | super(height) 38 | @width = height 39 | end 40 | 41 | def set_width(width) 42 | super(width) 43 | @height = width 44 | end 45 | end 46 | 47 | # Another common instance of a Liskov violation is raising an exception for an 48 | # overridden method in a child class. It’s also not uncommon to see methods 49 | # overridden with modified method signatures causing branching on type in 50 | # classes that depend on objects of the parent’s type. All of these either 51 | # lead to unstable code or unnecessary and ugly branching. 52 | 53 | class Animal 54 | def walk 55 | 'walking_as_animal' 56 | end 57 | end 58 | 59 | class Cat < Animal 60 | def run 61 | 'run_as_cat' 62 | end 63 | end 64 | 65 | # Solution 66 | 67 | class Animal 68 | def walk 69 | 'walking_as_animal' 70 | end 71 | 72 | def run 73 | raise NotImplementedError 74 | end 75 | end 76 | 77 | class Cat < Animal 78 | def run 79 | 'run_as_cat' 80 | end 81 | end 82 | 83 | -------------------------------------------------------------------------------- /threads/mutex.rb: -------------------------------------------------------------------------------- 1 | # Mutexes provide a mechanism for multiply threads to synchronize access to a 2 | # critical portion of code. It helps bring some order and some guaranty to the 3 | # world of multi-threaded chaos. 4 | # In this program, since any thread has to lock the mutex before it can push to 5 | # the Array, there's a guarantee that no two threads will be performing this 6 | # operation at the same time. In other words, this operation can no longer be 7 | # interrupted before it's completed. Once one thread begins pushing to the 8 | # Array, no other threads will be able to enter that portion of code until the 9 | # first thread is finished. This operation is now thread-safe. 10 | 11 | shared_array = Array.new 12 | # Notice that the mutex is shared among all the threads. The guarantee only 13 | # works if the threads are sharing the same Mutex instance. In this way, when 14 | # one thread locks a mutex, others have to wait for it to be unlocked. 15 | mutex = Mutex.new 16 | 17 | 10.times.map do 18 | Thread.new do 19 | 1000.times do 20 | # Thread lock mutex and become owner, could be one one owner 21 | # Now only one thread can run wrapper code and update 22 | mutex.lock 23 | shared_array << nil 24 | mutex.unlock 25 | # After unlock mutex other threads can lock mutex 26 | 27 | # Other convenience method to do same 28 | # mutex.synchronize { shared_array << nil } 29 | end 30 | end 31 | end.each(&:join) 32 | 33 | # Here you can see that we have 10 * 10000 elements in array 34 | 35 | puts shared_array.size 36 | 37 | # Now all are same, because of mutex/ 38 | # The mutex sets up same boundaries for the thread. The first thread that hits 39 | # this bit of code will lock the mutex. it then becomes the owner of that mutex. 40 | # Until the owning thread unlocks the mutex, no other thread can lock it. 41 | 42 | 43 | # $ ruby => 10000 44 | # $ jruby => 10000 45 | # $ rbx => 1000 46 | 47 | -------------------------------------------------------------------------------- /structures/binary_search_tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class BinarySearchTree < BinaryTree 9 | 10 | include SearchTreeMethods 11 | 12 | def initialize(*args) 13 | super 14 | end 15 | 16 | def find(obj) 17 | return nil if empty? 18 | diff = obj <=> @key 19 | if diff == 0 20 | return @key 21 | elsif diff < 0 22 | return @left.find(obj) 23 | elsif diff > 0 24 | return @right.find(obj) 25 | end 26 | end 27 | 28 | def min 29 | if empty? 30 | return nil 31 | elsif @left.empty? 32 | return @key 33 | else 34 | return @left.min 35 | end 36 | end 37 | 38 | def insert(obj) 39 | if empty? 40 | attachKey(obj) 41 | else 42 | diff = obj <=> @key 43 | if diff == 0 44 | raise ArgumentError 45 | elsif diff < 0 46 | @left.insert(obj) 47 | elsif diff > 0 48 | @right.insert(obj) 49 | end 50 | end 51 | balance 52 | end 53 | 54 | def attachKey(obj) 55 | raise StateError if not empty? 56 | @key = obj 57 | @left = BinarySearchTree.new 58 | @right = BinarySearchTree.new 59 | end 60 | 61 | def balance 62 | end 63 | 64 | def withdraw(obj) 65 | raise ArgumentError if empty? 66 | diff = obj <=> @key 67 | if diff == 0 68 | if not @left.empty? 69 | max = @left.max 70 | @key = max 71 | @left.withdraw(max) 72 | elsif not @right.empty? 73 | min = @right.min 74 | @key = min 75 | @right.withdraw(min) 76 | else 77 | detachKey 78 | end 79 | elsif diff < 0 80 | @left.withdraw(obj) 81 | elsif diff > 0 82 | @right.withdraw(obj) 83 | end 84 | balance 85 | end 86 | 87 | end -------------------------------------------------------------------------------- /patterns/behavioral/interpreter.rb: -------------------------------------------------------------------------------- 1 | # This pattern provides an interpreter to deal with an abstract language. Using 2 | # classes we can understand the inputs for parse them 3 | 4 | # Parsing entity 5 | class Number 6 | attr_reader :value 7 | 8 | def initialize(value) 9 | @value = value 10 | end 11 | 12 | def execute 13 | value 14 | end 15 | end 16 | 17 | # Operation interface 18 | class Operation 19 | attr_reader :left, :right 20 | 21 | def initialize(left, right) 22 | @left = left 23 | @right = right 24 | end 25 | 26 | def execute 27 | raise NotImplementedError 28 | end 29 | end 30 | 31 | # Operations 32 | class Plus < Operation 33 | def execute 34 | left.execute + right.execute 35 | end 36 | end 37 | 38 | class Minus < Operation 39 | def execute 40 | left.execute - right.execute 41 | end 42 | end 43 | 44 | class Multiply < Operation 45 | def execute 46 | left.execute * right.execute 47 | end 48 | end 49 | 50 | class Devide < Operation 51 | def execute 52 | left.execute / right.execute 53 | end 54 | end 55 | 56 | # Interpreter class 57 | class Interpreter 58 | def self.parse(input) 59 | stack = [] 60 | 61 | input.lstrip! 62 | while input.length > 0 63 | case input 64 | when /\A-?\d+(\.\d+)?/ 65 | stack << Number.new($&.to_i) 66 | else 67 | second, first = stack.pop(), stack.pop() 68 | 69 | case input 70 | when /\A\+/ 71 | stack << Plus.new(first, second) 72 | when /\A\-/ 73 | stack << Minus.new(first, second) 74 | when /\A\*/ 75 | stack << Multiply.new(first, second) 76 | else 77 | raise SyntaxError 78 | end 79 | end 80 | 81 | input = $' 82 | input.lstrip! 83 | end 84 | 85 | raise SyntaxError unless stack.size == 1 86 | 87 | stack.first.execute 88 | end 89 | end 90 | 91 | # Usage 92 | puts Interpreter.parse('1 + 1 + 2 - 1') # => 3 93 | 94 | -------------------------------------------------------------------------------- /structures/ordered_list_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class OrderedListAsLinkedList < OrderedList 9 | 10 | def initialize 11 | super 12 | @linkedList = SinglyLinkedList.new 13 | end 14 | 15 | attr_reader :linkedList 16 | 17 | attr_accessor :count 18 | 19 | def insert(obj) 20 | @linkedList.append(obj) 21 | @count += 1 22 | end 23 | 24 | def [](offset) 25 | raise IndexError if not (0 ... @count) === offset 26 | ptr = @linkedList.head 27 | i = 0 28 | while i < offset and not ptr.nil? 29 | ptr = ptr.succ 30 | i += 1 31 | end 32 | ptr.datum 33 | end 34 | 35 | def member?(obj) 36 | ptr = @linkedList.head 37 | while not ptr.nil? 38 | return true if ptr.datum.equal?(obj) 39 | ptr = ptr.succ 40 | end 41 | false 42 | end 43 | 44 | def find(arg) 45 | ptr = @linkedList.head 46 | while not ptr.nil? 47 | return ptr.datum if ptr.datum == arg 48 | ptr = ptr.succ 49 | end 50 | nil 51 | end 52 | 53 | def withdraw(obj) 54 | raise ContainerEmpty if @count == 0 55 | @linkedList.extract(obj) 56 | @count -= 1 57 | end 58 | 59 | def findPosition(obj) 60 | ptr = @linkedList.head 61 | while not ptr.nil? 62 | break if ptr.datum == obj 63 | ptr = ptr.succ 64 | end 65 | Cursor.new(self, ptr) 66 | end 67 | 68 | class Cursor 69 | 70 | def initialize(list, element) 71 | @list = list 72 | @element = element 73 | end 74 | 75 | def datum 76 | @element.datum 77 | end 78 | 79 | def insertAfter(obj) 80 | @element.insertAfter(obj) 81 | @list.count += 1 82 | end 83 | 84 | def withdraw 85 | @list.linkedList.extract(@element.datum) 86 | @list.count -= 1 87 | end 88 | end 89 | 90 | end -------------------------------------------------------------------------------- /patterns/structural/flyweight.rb: -------------------------------------------------------------------------------- 1 | # The Flyweight pattern describes how to share objects to allow their use at 2 | # fine granularity without prohibitive cost. 3 | 4 | # Flyweight Object 5 | class Lamp 6 | attr_reader :color 7 | 8 | def initialize(color) 9 | @color = color 10 | end 11 | end 12 | 13 | class TreeBranch 14 | attr_reader :branch_number 15 | 16 | def initialize(branch_number) 17 | @branch_number = branch_number 18 | end 19 | 20 | def hang(lamp) 21 | puts "Hang #{lamp.color} lamp on branch #{branch_number}" 22 | end 23 | end 24 | 25 | # Flyweight Factory 26 | class LampFactory 27 | attr_reader :lamps 28 | 29 | def initialize 30 | @lamps = {} 31 | end 32 | 33 | def find_lamp(color) 34 | if lamps.has_key?(color) 35 | lamp = lamps[color] 36 | else 37 | lamp = Lamp.new(color) 38 | lamps[color] = lamp 39 | end 40 | lamp 41 | end 42 | 43 | def total_number_of_lamps_made 44 | lamps.size 45 | end 46 | end 47 | 48 | class ChristmasTree 49 | attr_reader :lamp_factory, :lamps_hung 50 | 51 | def initialize(lamp_factory = LampFactory.new) 52 | @lamp_factory = lamp_factory 53 | @lamps_hung = 0 54 | 55 | dress_up_the_tree 56 | end 57 | 58 | def hang_lamp(color, branch_number) 59 | TreeBranch.new(branch_number).hang(lamp_factory.find_lamp(color)) 60 | lamps_hung += 1 61 | end 62 | 63 | def dress_up_the_tree(&block) 64 | yield self if block_given? 65 | end 66 | end 67 | 68 | # Usage 69 | christmas_tree = ChristmasTree.new 70 | christmas_tree.dress_up_the_tree { 71 | hang_lamp('red', 1) 72 | hang_lamp('blue', 1) 73 | hang_lamp('yellow', 1) 74 | hang_lamp('red', 2) 75 | hang_lamp('blue', 2) 76 | hang_lamp('yellow', 2) 77 | hang_lamp('red', 3) 78 | hang_lamp('blue', 3) 79 | hang_lamp('yellow', 3) 80 | hang_lamp('red', 4) 81 | hang_lamp('blue', 4) 82 | hang_lamp('yellow', 4) 83 | hang_lamp('red', 5) 84 | hang_lamp('blue', 5) 85 | hang_lamp('yellow', 5) 86 | hang_lamp('red', 6) 87 | hang_lamp('blue', 6) 88 | hang_lamp('yellow', 6) 89 | hang_lamp('red', 7) 90 | hang_lamp('blue', 7) 91 | hang_lamp('yellow', 7) 92 | } 93 | -------------------------------------------------------------------------------- /patterns/behavioral/mediator.rb: -------------------------------------------------------------------------------- 1 | # The essence of the Mediator Pattern is to "define an object that encapsulates 2 | # how a set of objects interact". It promotes loose coupling by keeping objects 3 | # from referring to each other explicitly, and it allows their interaction to be 4 | # varied independently. Client classes can use the mediator to send messages to 5 | # other clients, and can receive messages from other clients via an event on the 6 | # mediator class. 7 | 8 | # Classes witch interact with mediator 9 | class Buyer 10 | attr_accessor :balance 11 | 12 | def initialize(balance) 13 | @balance = balance 14 | end 15 | 16 | def use_agency_for_buying_house(agency) 17 | @agency = agency 18 | end 19 | 20 | def buy(house) 21 | @agency.purchase(house, self) 22 | end 23 | end 24 | 25 | class Seller 26 | attr_accessor :balance 27 | 28 | def initialize(balance = 0) 29 | @balance = balance 30 | end 31 | 32 | def use_agency_for_selling_house(agency, house) 33 | agency.register house 34 | end 35 | end 36 | 37 | class House 38 | attr_reader :price, :owner 39 | 40 | def initialize(price, owner) 41 | @price = price 42 | @owner = owner 43 | end 44 | 45 | def new_owner(buyer) 46 | @owner = buyer 47 | end 48 | end 49 | 50 | # Mediator class 51 | class Agency 52 | def initialize 53 | @houses = [] 54 | end 55 | 56 | def register(house) 57 | @houses << house 58 | end 59 | 60 | def purchase(house, buyer) 61 | if can_make_deal? house, buyer 62 | make_payment house, buyer 63 | change_owner house, buyer 64 | end 65 | end 66 | 67 | private 68 | 69 | def can_make_deal?(house, buyer) 70 | @houses.include?(house) && buyer.balance >= house.price 71 | end 72 | 73 | def make_payment(house, buyer) 74 | house.owner.balance += house.price 75 | buyer.balance -= house.price 76 | end 77 | 78 | def change_owner(house, buyer) 79 | house.new_owner buyer 80 | end 81 | end 82 | 83 | # Usage 84 | buyer = Buyer.new(1000) 85 | 86 | seller = Seller.new(0) 87 | house = House.new(100, seller) 88 | 89 | agency = Agency.new 90 | buyer.use_agency_for_buying_house agency 91 | seller.use_agency_for_selling_house agency, house 92 | 93 | buyer.buy house 94 | 95 | puts house.inspect 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /solid/open_close.rb: -------------------------------------------------------------------------------- 1 | # The Open/Closed Principle states that classes or methods should be open for 2 | # extension, but closed for modification. This tells us we should strive for 3 | # modular designs that make it possible for us to change the behavior of the 4 | # system without making modifications to the classes themselves. This is 5 | # generally achieved through the use of patterns such as the strategy pattern. 6 | 7 | # In the below example we can see that we’ll have to modify our file parser 8 | # anytime we add a client that reports usage information to us in a different 9 | # file format. This violates the Open/Closed Principle. 10 | 11 | class FileParser 12 | attr_reader :file 13 | 14 | def initialize(file) 15 | @file = file 16 | end 17 | 18 | def parse 19 | # If we want add new parser we must to edit this method and in private method 20 | case file.format 21 | when :xml 22 | parse_xml 23 | when :cvs 24 | parse_cvs 25 | # when :json 26 | # parse_json 27 | else 28 | # Implementation 29 | end 30 | 31 | end 32 | 33 | private 34 | 35 | def parse_xml 36 | # Implementation 37 | end 38 | 39 | def parse_cvs 40 | # Implementation 41 | end 42 | 43 | # New parse method 44 | # def parse_json 45 | # # Implementation 46 | # end 47 | end 48 | 49 | # Solution 50 | 51 | # With this refactor we’ve made it possible to add new parsers without changing any code. Any additional behavior will only require the addition of a new handler. This makes our FileParser reusable and in many cases will keep us in compliance with the Single Responsibility Principle as well by encouraging us to create smaller more focused classes. 52 | 53 | class FileParser 54 | attr_reader :parser 55 | 56 | def initialize(parser) 57 | @parser = parser 58 | end 59 | 60 | def parse(file) 61 | # Now if we want new parser just write new Class and pass it to method 62 | Data.new(parser.parse file) 63 | end 64 | end 65 | 66 | class JsonParser 67 | # We write new class for extension solution. 68 | def self.parse(file) 69 | # Implementation 70 | end 71 | end 72 | 73 | class XmlParser 74 | def self.parse(file) 75 | # Implementation 76 | end 77 | end 78 | 79 | class CvsParser 80 | def self.parse(file) 81 | # Implementation 82 | end 83 | end 84 | 85 | -------------------------------------------------------------------------------- /solid/interface_segregation.rb: -------------------------------------------------------------------------------- 1 | # The principle states that a client should not be forced to depend on methods 2 | # that it does not use 3 | 4 | # In this example, there are Computer, Programmer and Technician classes. Both, 5 | # Programmer and Technician use the Computer in a different way. The programmer 6 | # uses the computer for typing, but the technician knows how to change the 7 | # computer hard drive. What Interface Segregation Principle (ISP) enforces is 8 | # that one class should not depend on methods it does not use. 9 | # In our case, Programmer is unnecessarily coupled to the 10 | # Computer#change_hard_drive method because it does not use it, but the state 11 | # changes that this method enforces can affect the Programmer. Let's refactor 12 | # the code to obey the LSP. 13 | 14 | class Computer 15 | def turn_on 16 | # Implementation 17 | end 18 | 19 | def type 20 | # Implementation 21 | end 22 | 23 | def change_hard_drive 24 | # Implementation 25 | end 26 | end 27 | 28 | class User 29 | attr_reader :computer 30 | 31 | def initialize(computer) 32 | @computer = computer 33 | end 34 | end 35 | 36 | class Programmer < User 37 | def use_computer 38 | computer.turn_on 39 | computer.type 40 | end 41 | end 42 | 43 | class Technician < User 44 | def fix_computer 45 | computer.change_hard_drive 46 | end 47 | end 48 | 49 | # Solution 50 | 51 | # After this refactor the Technician uses a different object from the type 52 | # ComputerInternals which is isolated from the state of the Computer. The state 53 | # of the Computer object can be influenced by the Programmer but the changes 54 | # wont affect the Technician in any way. 55 | 56 | class Computer 57 | def turn_on 58 | # Implementation 59 | end 60 | 61 | def type 62 | # Implementation 63 | end 64 | end 65 | 66 | class ComputerInternals 67 | def change_hard_drive 68 | # Implementation 69 | end 70 | end 71 | 72 | class Programmer 73 | attr_reader :computer 74 | 75 | def initialize(computer) 76 | @computer = computer 77 | end 78 | 79 | def use_computer 80 | computer.turn_on 81 | computer.type 82 | end 83 | end 84 | 85 | class Technician 86 | attr_reader :computer_internals 87 | 88 | def initialize(computer_internals) 89 | @computer_internals = computer_internals 90 | end 91 | 92 | def fix_computer 93 | computer_internals.change_hard_drive 94 | end 95 | end 96 | 97 | -------------------------------------------------------------------------------- /structures/ordered_list_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class OrderedListAsArray < OrderedList 9 | 10 | def initialize(size = 0) 11 | super() 12 | @array = Array.new(size) 13 | end 14 | 15 | attr_reader :array 16 | 17 | attr_accessor :count 18 | 19 | def insert(obj) 20 | raise ContainerFull if @count == @array.length 21 | @array[@count] = obj 22 | @count += 1 23 | end 24 | 25 | def member?(obj) 26 | for i in 0 ... @count 27 | return true if @array[i].equal?(obj) 28 | end 29 | false 30 | end 31 | 32 | def find(obj) 33 | for i in 0 ... @count 34 | return @array[i] if @array[i] == obj 35 | end 36 | nil 37 | end 38 | 39 | def withdraw(obj) 40 | raise ContainerEmpty if @count == 0 41 | i = 0 42 | while i < @count and not @array[i].equal?(obj) 43 | i += 1 44 | end 45 | raise ArgumentError if i == @count 46 | while i < @count - 1 47 | @array[i] = @array[i + 1] 48 | i += 1 49 | end 50 | @array[i] = nil 51 | @count -= 1 52 | end 53 | 54 | class Cursor 55 | 56 | def initialize(list, offset) 57 | super() 58 | @list = list 59 | @offset = offset 60 | end 61 | 62 | def datum 63 | raise IndexError if not (0 ... @list.count) === offset 64 | @list[@offset] 65 | end 66 | 67 | end 68 | 69 | def findPosition(obj) 70 | i = 0 71 | while i < @count and not @array[i] == obj 72 | i += 1 73 | end 74 | Cursor.new(self, i) 75 | end 76 | 77 | def [](offset) 78 | raise IndexError if not (0 ... @count) === offset 79 | @array[offset] 80 | end 81 | 82 | class Cursor < Cursor 83 | 84 | def insertAfter(obj) 85 | raise IndexError if not (0 ... @list.count) === @offset 86 | raise ContainerFull if @list.count == @list.array.length 87 | insertPosition = @offset + 1 88 | i = @list.count 89 | while i > insertPosition 90 | @list.array[i] = @list.array[i - 1] 91 | i -= 1 92 | end 93 | @list.array[insertPosition] = obj 94 | @list.count += 1 95 | end 96 | 97 | def withdraw 98 | raise IndexError if not (0 ... @list.count) === @offset 99 | raise ContainerEmpty if @list.count == 0 100 | i = @offset 101 | while i < @list.count - 1 102 | @list.array[i] = @list.array[i + 1] 103 | i += 1 104 | end 105 | @list.array[i] = nil 106 | @list.count -= 1 107 | end 108 | 109 | end 110 | 111 | end 112 | 113 | -------------------------------------------------------------------------------- /structures/singly_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class SinglyLinkedList 9 | class Element 10 | attr_accessor :data, :succ 11 | 12 | def initialize(list, data, succ) 13 | @list, @data, @succ = list, data, succ 14 | end 15 | 16 | def insert_after(item) 17 | @succ = Element.new(@list, item, @succ) 18 | 19 | if @list.tail.equal?(self) 20 | @list.tail = @succ 21 | end 22 | end 23 | 24 | def insert_before(item) 25 | tmp = Element.new(@list, item, self) 26 | 27 | if @list.head.equal?(self) 28 | @list.head = tmp 29 | else 30 | prevElem = @list.head 31 | while not prevElem.nil? and not prevElem.succ.equal?(self) 32 | prevElem = prevElem.succ 33 | end 34 | prevElem.succ = tmp 35 | end 36 | end 37 | end 38 | 39 | attr_accessor :head, :tail 40 | 41 | def initialize 42 | @head = nil 43 | @tail = nil 44 | end 45 | 46 | def purge 47 | @head = nil 48 | @tail = nil 49 | end 50 | 51 | def empty? 52 | @head.nil? 53 | end 54 | 55 | def first 56 | if @head.nil? 57 | raise Error 58 | else 59 | @head.data 60 | end 61 | end 62 | 63 | def last 64 | if @tail.nil? 65 | raise Error 66 | else 67 | @tail.data 68 | end 69 | end 70 | 71 | def prepend(elem) 72 | temp = Element.new(self, elem, @head) 73 | 74 | @tail = temp if @head.nil? 75 | @head = temp 76 | end 77 | 78 | def append(elem) 79 | temp = Element.new(self, elem, nil) 80 | 81 | if @head.nil? 82 | @head = temp 83 | else 84 | @tail.succ = temp 85 | end 86 | 87 | @tail = temp 88 | end 89 | 90 | def clone 91 | linked_list = SinglyLinkedList.new 92 | 93 | elem = @head 94 | while not elem.nil? 95 | linked_list.append elem.data 96 | elem = @head.succ 97 | end 98 | 99 | linked_list 100 | end 101 | 102 | def extract 103 | elem = @head 104 | 105 | prevElem = nil 106 | while not elem.nil? and not elem.data.equal?(item) 107 | prevElem = elem 108 | elem = elem.succ 109 | end 110 | 111 | raise ArgumentError if elem.nil? 112 | 113 | if elem == @head 114 | @head = elem.succ 115 | else 116 | prevElem.succ = elem.succ 117 | end 118 | 119 | if elem == @tail 120 | @tail = prevElem 121 | end 122 | end 123 | 124 | def each 125 | elem = @head 126 | while not elem.nil? 127 | yield ptr.data 128 | elem = elem.succ 129 | end 130 | end 131 | end -------------------------------------------------------------------------------- /solid/single_responsibility.rb: -------------------------------------------------------------------------------- 1 | # The Single Responsibility Principle is the most abstract of the bunch. It 2 | # helps keep classes and methods small and maintainable. In addition to keeping 3 | # classes small and focused it also makes them easier to understand. 4 | 5 | # While we all agree that focusing on a single responsibility is important, it’s 6 | # difficult to determine what a class’s responsibility is. Generally, it is said 7 | # that anything that gives a class a reason to change can be viewed as a 8 | # responsibility. By change I am talking about structural changes to the class 9 | # itself (as in modifying the code in the class’s file, not the object’s 10 | # in-memory state). 11 | # In the below class we have a single command interface that processes 12 | # commission payments for deals. At first glance the class seems simple enough, 13 | # but let’s look at reasons we might want to change this class. Any change in 14 | # how we calculate commissions would require a change to this class. We could 15 | # introduce new commission rules or strategies that would cause our 16 | # calculate_commission method to change. For instance, we might want to vary 17 | # the percentage based on deal amount. Any change in the steps required to mark 18 | # a deal as processed in the mark_deal_processed method would result in a change 19 | # in the file as well. An example of this might be adding support for sending an 20 | # email summary of a specific person’s commissions after marking a deal 21 | # processed. The fact that we can identify multiple reasons to change signals a 22 | # violation of the Single Responsibility Principle. 23 | 24 | class DealProcessor 25 | attr_reader :deals 26 | 27 | def initialize(deals) 28 | @deals = deals 29 | end 30 | 31 | def process 32 | deals.each do |deal| 33 | # Here we calculate commission and create instance of Commission 34 | Commission.create(deal: deal, amount: calculate_commission(deal)) 35 | mark_deal_processed 36 | end 37 | end 38 | 39 | private 40 | 41 | def mark_deal_processed 42 | # Implementation 43 | end 44 | 45 | def calculate_commission(deal) 46 | deal.amount * 0.05 47 | end 48 | end 49 | 50 | class Commission 51 | # Implementation 52 | end 53 | 54 | # Solution 55 | 56 | class DealProcessor 57 | attr_reader :deals 58 | 59 | def initialize(deals) 60 | @deals = deals 61 | end 62 | 63 | def process 64 | deals.each do |deal| 65 | # Now we call calculator in one operation, all logic now in it 66 | CommissionCalculator.create_commission(deal) if mark_deal_processed 67 | end 68 | end 69 | 70 | private 71 | 72 | def mark_deal_processed 73 | # Implementation 74 | end 75 | end 76 | 77 | class CommissionCalculator 78 | def self.create_commission(deal) 79 | Commission.new(deal: deal, amount: calculate(deal)) 80 | end 81 | 82 | private 83 | 84 | def self.calculate(deal) 85 | deal.amount * 0.05 86 | end 87 | end 88 | 89 | class Commission 90 | # Implementation 91 | end 92 | 93 | -------------------------------------------------------------------------------- /threads/rails.rb: -------------------------------------------------------------------------------- 1 | # config.threadsafe!: what does it do? 2 | # Let’s take a look at the threadsafe! method: 3 | 4 | def threadsafe! 5 | @preload_frameworks = true 6 | @cache_classes = true 7 | @dependency_loading = false 8 | @allow_concurrency = true 9 | self 10 | end 11 | 12 | # Calling this method sets four options in our app configuration. Let’s walk 13 | # through each option and talk about what it does. 14 | 15 | # Preloading Frameworks 16 | # The first option @preload_frameworks does pretty much what it says, it forces 17 | # the Rails framework to be eagerly loaded on boot. When this option is not 18 | # enabled, framework classes are loaded lazily via autoload. In multi-threaded 19 | # environments, the framework needs to be eagerly loaded before any threads are 20 | # created because of thread safety issues with autoload. We know that loading 21 | # the framework isn’t threadsafe, so the strategy is to load it all up before 22 | # any threads are ready to handle requests. 23 | 24 | # Caching classes 25 | # The @cache_classes option controls whether or not classes get reloaded. 26 | # Remember when you’re doing “TDD” in your application? You modify a controller, 27 | # then reload the page to “test” it and see that things changed? Ya, that’s what 28 | # this option controls. When this option is false, as in development, your 29 | # classes will be reloaded when they are modified. Without this option, we 30 | # wouldn’t be able to do our “F5DD” (yes, that’s F5 Driven Development). 31 | # In production, we know that classes aren’t going to be modified on the fly, 32 | # so doing the work to figure out whether or not to reload classes is just 33 | # wasting resources, so it makes sense to never reload class definitions. 34 | 35 | # Dependency loading 36 | # This option, @dependency_loading controls code loading when missing constants 37 | # are encountered. For example, a controller references the User model, but the 38 | # User constant isn’t defined. In that case, if @dependency_loading is true, 39 | # Rails will find the file that contains the User constant, and load that file. 40 | # We already talked about how code loading is not thread safe, so the idea here 41 | # is that we should load the framework, then load all user code, then disable 42 | # dependency loading. Once dependency loading is disabled, framework code and 43 | # app code should be loaded, and any missing constants will just raise an 44 | # exception rather than attempt to load code. 45 | # We justify disabling this option in production because (as was mentioned 46 | # earlier) code loading is not threadsafe, and we expect to have all code loaded 47 | # before any threads can handle requests. 48 | 49 | # Allowing concurrency 50 | # @allow_concurrency option controls whether or not the Rack::Lock middleware 51 | # is used in your stack. Rack::Lock wraps a mutex around your request. The idea 52 | # being that if you have code that is not threadsafe, this mutex will prevent 53 | # multiple threads from executing your controller code at the same time. When 54 | # threadsafe! is set, this middleware is removed, and controller code can be 55 | # executed in parallel. 56 | 57 | -------------------------------------------------------------------------------- /patterns/behavioral/state.rb: -------------------------------------------------------------------------------- 1 | # Allow an object to alter its behavior when its internal state changes. 2 | # The object will appear to change its class. 3 | 4 | # Each call to state defines a new subclass of Connection that is stored in a 5 | # hash. Then, a call to transition_to instantiates one of these subclasses and 6 | # sets it to the be the active state. Method calls to Connection are delegated 7 | # to the active state object via method_missing 8 | 9 | 10 | module StatePattern 11 | class UnknownStateException < Exception 12 | end 13 | 14 | def StatePattern.included(base) 15 | base.extend StatePattern::ClassMethods 16 | end 17 | 18 | module ClassMethods 19 | attr_reader :state_classes 20 | 21 | def state(state_name, &block) 22 | @state_classes ||= {} 23 | 24 | new_klass = Class.new(self, &block) 25 | new_klass.class_eval do 26 | alias_method :__old_init, :initialize 27 | 28 | def initialize(context, *args, &block) 29 | @context = context 30 | __old_init(*args, &block) 31 | end 32 | end 33 | 34 | @state_classes[state_name] = new_klass 35 | end 36 | end 37 | 38 | attr_accessor :current_state, :current_state_obj 39 | 40 | def transition_to(state_name, *args, &block) 41 | new_context = @context || self 42 | klass = new_context.class.state_classes[state_name] 43 | if klass 44 | new_context.current_state = state_name 45 | new_context.current_state_obj = klass.new(new_context, *args, &block) 46 | else 47 | raise UnknownStateException,"tried to transition to unknown state,#{state_name}" 48 | end 49 | end 50 | 51 | def method_missing(method, *args, &block) 52 | unless @current_state_obj 53 | transition_to :initial 54 | end 55 | 56 | if @current_state_obj 57 | @current_state_obj.send(method, *args, &block) 58 | else 59 | super 60 | end 61 | end 62 | end 63 | 64 | class Connection 65 | include StatePattern 66 | 67 | # you always need a state named initial 68 | state :initial do 69 | 70 | def connect 71 | # move to state :connected. all other args to transition_to# are passed 72 | # to the new state's constructor transition_to:connected, "hello from 73 | # initial state" 74 | puts "connected" 75 | end 76 | 77 | def disconnect 78 | puts "not connected yet" 79 | end 80 | end 81 | 82 | state :connected do 83 | def initialize(msg) 84 | puts "initialize got msg:#{msg}" 85 | end 86 | 87 | def connect 88 | puts "already connected" 89 | end 90 | def disconnect 91 | puts "disconnecting" 92 | transition_to :initial 93 | end 94 | end 95 | 96 | def reset 97 | # you can also change the state from outside of the state objects 98 | # transition_to:initial 99 | puts "resetting outside a state" 100 | end 101 | end 102 | 103 | # Usage 104 | c = Connection.new 105 | c.disconnect # => not connected yet 106 | c.connect # => connected, initialize got msg: hello from initial state 107 | c.connect # => already connected 108 | c.disconnect # => disconnecting 109 | c.connect # => connected, initialize got msg: hello from initial state 110 | c.reset # => reseting outside a state 111 | c.disconnect # => not connected yet -------------------------------------------------------------------------------- /structures/m_way_tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class MWayTree < SearchTree 9 | 10 | def initialize(m) 11 | assert { m > 2 } 12 | super() 13 | @key = Array.new(m - 1, 1) 14 | @subtree = Array.new(m) 15 | end 16 | 17 | def m 18 | @subtree.length 19 | end 20 | 21 | def depthFirstTraversal(&block) 22 | if not empty? 23 | for i in 1 .. @count 24 | yield (@key[i], PREVISIT) 25 | end 26 | @subtree[0].depthFirstTraversal(&block) 27 | for i in 1 .. @count 28 | yield (@key[i], INVISIT) 29 | @subtree[i].depthFirstTraversal(&block) 30 | end 31 | for i in 1 .. @count 32 | yield (@key[i], POSTVISIT) 33 | end 34 | end 35 | end 36 | 37 | def find(obj) 38 | return nil if empty? 39 | i = @count 40 | while i > 0 41 | diff = obj <=> @key[i] 42 | return @key[i] if diff == 0 43 | break if diff > 0 44 | i = i - 1 45 | end 46 | return @subtree[i].find(obj) 47 | end 48 | 49 | def findIndex(obj) 50 | return 0 if empty? or obj < @key[1] 51 | left = 1 52 | right = @count 53 | while left < right 54 | middle = (left + right + 1) / 2 55 | if obj < @key[middle] 56 | right = middle - 1 57 | else 58 | left = middle 59 | end 60 | end 61 | return left 62 | end 63 | 64 | def find(obj) 65 | return nil if empty? 66 | index = findIndex(obj) 67 | if index != 0 and @key[index] == obj 68 | return @key[index] 69 | else 70 | return @subtree[index].find(obj) 71 | end 72 | end 73 | 74 | def insert(obj) 75 | if empty? 76 | @subtree[0] = MWayTree.new(m) 77 | @key[1] = obj 78 | @subtree[1] = MWayTree.new(m) 79 | @count = 1 80 | else 81 | index = findIndex(obj) 82 | raise ValueError if index != 0 and @key[index] == obj 83 | if not full? 84 | i = @count 85 | while i > index 86 | @key[i + 1] = @key[i] 87 | @subtree[i + 1] = @subtree[i] 88 | i = i - 1 89 | end 90 | @key[index + 1] = obj 91 | @subtree[index + 1] = MWayTree.new(m) 92 | @count = @count + 1 93 | else 94 | @subtree[index].insert(obj) 95 | end 96 | end 97 | end 98 | 99 | def withdraw(obj) 100 | raise ArgumentError if empty? 101 | index = findIndex(obj) 102 | if index != 0 and @key[index] == obj 103 | if not @subtree[index - 1].empty? 104 | max = @subtree[index - 1].max 105 | @key[index] = max 106 | @subtree[index - 1].withdraw(max) 107 | elsif not @subtree[index].empty? 108 | min = @subtree[index].min 109 | @key[index] = min 110 | @subtree[index].withdraw(min) 111 | else 112 | @count = @count - 1 113 | i = index 114 | while i <= @count 115 | @key[i] = @key[i + 1] 116 | @subtree[i] = @subtree[i + 1] 117 | i = i + 1 118 | end 119 | @key[i] = nil 120 | @subtree[i] = nil 121 | if @count == 0 122 | @subtree[0] = nil 123 | end 124 | end 125 | else 126 | @subtree[index].withdraw(obj) 127 | end 128 | end 129 | 130 | end -------------------------------------------------------------------------------- /patterns/behavioral/chain_of_responsibility.rb: -------------------------------------------------------------------------------- 1 | # Avoid coupling the sender of a request to its receiver by giving more than one 2 | # object a chance to handle the request. Chain the receiving objects and pass 3 | # the request along the chain until an object handles it. 4 | 5 | 6 | # Implements the chain of responsibility pattern. Does not know anything 7 | # about the approval process, merely whether the current handler can approve 8 | # the request, or must pass it to a successor. 9 | class PurchaseApprover 10 | attr_reader :successor 11 | 12 | def initialize(successor) 13 | @successor = successor 14 | end 15 | 16 | def process_request(request) 17 | if approve_request request 18 | return 19 | elsif successor 20 | successor.process_request request 21 | else 22 | deny_request request 23 | end 24 | end 25 | 26 | # This may be overridden by a handler if it wants to provide a custom action 27 | # when it is the last member of the chain 28 | def deny_request request 29 | # Implementation 30 | end 31 | end 32 | 33 | # Parts of approving chain 34 | # Base class for approvers who only consider whether the request amount is allowable 35 | class AmountApprover < PurchaseApprover 36 | BASE = 500 37 | 38 | def approve_request(request) 39 | if request.amount < self.class::ALLOWABLE 40 | print_approval request 41 | return true 42 | else 43 | return false 44 | end 45 | end 46 | end 47 | 48 | class Manager < AmountApprover 49 | ALLOWABLE = 10 * BASE 50 | 51 | def print_approval(request) 52 | puts "Manager will approve $#{request.amount}" 53 | end 54 | end 55 | 56 | class Director < AmountApprover 57 | ALLOWABLE = 20 * BASE 58 | 59 | def print_approval(request) 60 | puts "Director will approve $#{request.amount}" 61 | end 62 | end 63 | 64 | class VicePresident < AmountApprover 65 | ALLOWABLE = 40 * BASE 66 | 67 | def print_approval request 68 | puts "VicePresident will approve $#{request.amount}" 69 | end 70 | end 71 | 72 | class President < AmountApprover 73 | ALLOWABLE = 60 * BASE 74 | 75 | def print_approval(request) 76 | puts "President will approve $#{request.amount}" 77 | end 78 | end 79 | 80 | class ChiefFinancialOperations < PurchaseApprover 81 | # An example of a handler that does not inherit from AmountApprover 82 | def approve_request(request) 83 | if within_annual_budget? request 84 | puts "CFO will approve $#{request.amount}" 85 | return true 86 | else 87 | return false 88 | end 89 | end 90 | 91 | def within_annual_budget?(request) 92 | # Implementation 93 | end 94 | end 95 | 96 | class PurchaseRequest 97 | attr_reader :amount, :amount, :purpose 98 | 99 | def initialize(number, amount, purpose) 100 | @number = number 101 | @amount = amount 102 | @purpose = purpose 103 | end 104 | end 105 | 106 | # Realization of pattern class 107 | class CLP 108 | def initialize(*approvers) 109 | @authority = build_approvers(approvers).first 110 | end 111 | 112 | def process_request(request) 113 | @authority.process_request request 114 | end 115 | 116 | private 117 | 118 | def build_approvers(approver_classes) 119 | [].tap do |approvers| 120 | approver_classes.reverse.inject(nil) do |successor, approver| 121 | approver.new(successor).tap {|approver| approvers.unshift approver } 122 | end 123 | end 124 | end 125 | end 126 | 127 | # Usage 128 | approvers = CLP.new(Manager, Director, VicePresident, President) 129 | approvers.process_request PurchaseRequest.new(0, amount, 'General') 130 | -------------------------------------------------------------------------------- /patterns/structural/proxy.rb: -------------------------------------------------------------------------------- 1 | # When building a proxy, we could implement a method for each method in the 2 | # underlying object. However, this leads to a lot of repeated code and tightly 3 | # couples the proxy with the underlying object. A better alternative is to pass 4 | # method calls direcly to the underlying object. Ruby includes a method that is 5 | # perfect for this situation called method_missing. 6 | 7 | # Object witch is decorated 8 | class BankAccount 9 | attr_reader :balance 10 | 11 | def initialize(balance = 0) 12 | @balance = balance 13 | end 14 | 15 | def deposit(amount) 16 | balance += amount 17 | end 18 | 19 | def withdraw(amount) 20 | balance -= amount 21 | end 22 | end 23 | 24 | # Proxy 25 | # Do not use attr_reader for accessing to proxy object 26 | class BankAccountProxy 27 | def initialize(real_object) 28 | @real_object = real_object 29 | end 30 | 31 | def balance 32 | @real_object.balance 33 | end 34 | 35 | def deposit(amount) 36 | @real_object.deposit(amount) 37 | end 38 | 39 | def withdraw(amount) 40 | @real_object.withdraw(amount) 41 | end 42 | end 43 | 44 | # Protection proxy 45 | # Are you working on an MNC? If so, we might be well aware of the proxy server 46 | # that provides us internet by restricting access to some sort of websites like 47 | # public e-mail, social networking, data storage etc. The management feels that, 48 | # it is better to block some content and provide only work related web pages. 49 | # Proxy server does that job. This is a type of proxy design pattern 50 | class BankAccountProtectionProxy 51 | def initialize(real_account, owner_name) 52 | @subject = real_account 53 | @owner_name = owner_name 54 | end 55 | 56 | def deposit(amount) 57 | check_access 58 | @subject.deposit(amount) 59 | end 60 | 61 | def withdraw(amount) 62 | check_access 63 | @subject.withdraw(amount) 64 | end 65 | 66 | def balance 67 | check_access 68 | @subject.balance 69 | end 70 | 71 | def check_access 72 | # Implementation 73 | end 74 | end 75 | 76 | # Virtual proxy 77 | # In place of a complex or heavy object, use a skeleton representation. When an 78 | # underlying image is huge in size, just represent it using a virtual proxy 79 | # object and on demand load the real object. You know that the real object is 80 | # expensive in terms of instantiation and so without the real need we are not 81 | # going to use the real object. Until the need arises we will use the virtual proxy. 82 | class BankAccountVirtualProxy 83 | def initialize(&creation_block) 84 | @creation_block = creation_block 85 | end 86 | 87 | def deposit(amount) 88 | subject.deposit(amount) 89 | end 90 | 91 | def withdraw(amount) 92 | subject.withdraw(amount) 93 | end 94 | 95 | def balance 96 | subject.balance 97 | end 98 | 99 | def subject 100 | @subject ||= @creation_block.call 101 | end 102 | end 103 | 104 | # Remote proxy 105 | # In distributed object communication, a local object represents a remote object 106 | # (one that belongs to a different address space). The local object is a proxy 107 | # for the remote object, and method invocation on the local object results in 108 | # remote method invocation on the remote object. Think of an ATM implementation, 109 | # it will hold proxy objects for bank information that exists in the remote server. 110 | 111 | # Server part 112 | class BankAccountRemoteProxy 113 | # implementation 114 | end 115 | 116 | URI = 'druby://path_to_domain:port' 117 | DRb.start_service(URI, BankAccountRemoteProxy.new) 118 | DRb.thread.join 119 | 120 | # Client part 121 | URI = 'druby://path_to_domain:port' 122 | proxy = DRbObject.new_with_uri(URI) 123 | proxy.balance 124 | -------------------------------------------------------------------------------- /structures/axioms.rb: -------------------------------------------------------------------------------- 1 | # 1 - The time required to fetch a reference to an object from memory is a constant, 2 | # T_fetch, and the time required to store a reference to an object in memory is 3 | # a constant, T_store 4 | 5 | y = x 6 | 7 | # According to Axiom, the assignment statement has running time T_fetch + T_store. 8 | # That is, the time taken to fetch the object reference from variable x is 9 | # T_fetch and the time taken to store that object reference in the variable y is 10 | # T_store. 11 | 12 | y = 1 13 | 14 | # Also has running time T_fetch + T_store. To see why this should be the case, 15 | # consider that the constant '1' names a Fixnum object with value one. Therefore, 16 | # we can expect the cost of fetching the reference to the object named 1 is the 17 | # same as that of fetching a reference to any other object. 18 | 19 | 20 | # 2 - The times required to perform elementary arithmetic operations, such as 21 | # addition, subtraction, multiplication, division, and comparison, are all 22 | # constants. These times are denoted by T_+, T_-, T_/, T_*, and T_<, respectively. 23 | 24 | y = y + 1 25 | 26 | # We can determine time of a statement like is 2 * T_fetch + T_+ + T_store. This 27 | # is because we need to fetch two object references from the variables y and 1; 28 | # perform the addition giving a new object whose value is the sum; and, store 29 | # a reference to the new object in the variable y. 30 | # Ruby syntax provides an alternative way to express the same computation: 31 | 32 | y += 1 33 | 34 | # We shall assume that the alternative requires exactly the same running time as 35 | # the original statement. 36 | 37 | 38 | # 3 - The time required to call a method is a constant, T_call, and the time 39 | # required to return from a method is a constant, T_return 40 | # The rationale for making the overhead associated with parameter passing the 41 | # same as the time to store an object reference is that the passing of an 42 | # argument is conceptually the same as assignment of the actual parameter value 43 | # to the formal parameter of the method. 44 | 45 | y = f(x) 46 | 47 | # According to Axiom, the running time of the statement would be 48 | # T_fetch + 2 * T_store + T_call + T_f(x), where T_f(x) is the running time of 49 | # method f for input x. The first of the two stores is due to the passing of the 50 | # parameter x to the method f; the second arises from the assignment to the variable y. 51 | 52 | 53 | # 4 - The time required for the address calculation implied by an array 54 | # subscripting operation, e.g., a[i], is a constant, T_[]. This time does not 55 | # include the time to compute the subscript expression, nor does it include the 56 | # time to access the array element. 57 | 58 | y = a[i] 59 | 60 | # This is 3 * T_fetch Three operand fetches are required: the first to fetch a 61 | # reference to the array object a; the second to fetch a reference to the index 62 | # object i; and, the third to fetch a reference to the array element a[i]. 63 | 64 | 65 | # 5 - The time required to create a new object instance of a class is a constant, 66 | # T_new. This time does not include any time taken to initialize the object. 67 | # By applying Axioms we can determine that the running time of the statement 68 | 69 | i = Fixnum.new(0) 70 | 71 | # is T_new + T_fetch + 2 * T_store + T_call + T_fixnum_init, where T_fixnum_init 72 | # is the running time of the initialize method of the class Fixnum. 73 | 74 | 75 | # Example - In this section we apply Axioms, the analysis of the running time of a 76 | # program to compute the following simple arithmetic series summation 77 | 78 | def sum(n) 79 | result = 0 # T_fetch + T_store 80 | i = 1 # T_fetch + T_store 81 | while i <= n # (2 * T_fetch + T_<) * (n - 1) 82 | result += i # (2 * T_fetch + T_+ + T_store) * n 83 | i += 1 # (2 * T_fetch + T_+ + T_store) * n 84 | end 85 | return result # T_return + T_fetch 86 | end 87 | 88 | # Total: (6 * T_fetch + 2 * T_store + T_< + 2 * T_+) * n + (5 * T_fetch + 2 * T_store + T_< + T_return) 89 | # T(n) = t1 * n + t2 -------------------------------------------------------------------------------- /structures/tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class PrePostVisitor < Visitor 9 | 10 | abstractmethod :preVisit 11 | 12 | abstractmethod :inVisit 13 | 14 | abstractmethod :postVisit 15 | 16 | alias_method :visit, :inVisit 17 | 18 | end 19 | 20 | class PreOrder < PrePostVisitor 21 | 22 | def initialize(visitor) 23 | super() 24 | @visitor = visitor 25 | end 26 | 27 | def preVisit(obj) 28 | @visitor.visit(obj) 29 | end 30 | 31 | def inVisit(obj) 32 | end 33 | 34 | def postVisit(obj) 35 | end 36 | 37 | def done? 38 | @visitor.done? 39 | end 40 | 41 | end 42 | 43 | class InOrder < PrePostVisitor 44 | 45 | def initialize(visitor) 46 | super() 47 | @visitor = visitor 48 | end 49 | 50 | def preVisit(obj) 51 | end 52 | 53 | def inVisit(obj) 54 | @visitor.visit(obj) 55 | end 56 | 57 | def postVisit(obj) 58 | end 59 | 60 | def done? 61 | @visitor.done? 62 | end 63 | end 64 | 65 | class PostOrder < PrePostVisitor 66 | 67 | def initialize(visitor) 68 | super() 69 | @visitor = visitor 70 | end 71 | 72 | def preVisit(obj) 73 | end 74 | 75 | def inVisit(obj) 76 | end 77 | 78 | def postVisit(obj) 79 | @visitor.visit(obj) 80 | end 81 | 82 | def done? 83 | @visitor.done? 84 | end 85 | end 86 | 87 | class Tree < Container 88 | 89 | def initialize 90 | super 91 | end 92 | 93 | abstractmethod :key 94 | 95 | abstractmethod :getSubtree 96 | 97 | abstractmethod :empty? 98 | 99 | abstractmethod :leaf? 100 | 101 | abstractmethod :degree 102 | 103 | abstractmethod :height 104 | 105 | 106 | PREVISIT = -1 107 | INVISIT = 0 108 | POSTVISIT = +1 109 | 110 | def depthFirstTraversal(&block) 111 | if not empty? 112 | yield (key, PREVISIT) 113 | for i in 0 ... degree 114 | getSubtree(i).depthFirstTraversal(&block) 115 | end 116 | yield (key, POSTVISIT) 117 | end 118 | end 119 | 120 | def depthFirstTraversalAccept(visitor) 121 | assert { visitor.is_a?(PrePostVisitor) } 122 | depthFirstTraversal do |obj, mode| 123 | break if visitor.done? 124 | case mode 125 | when PREVISIT 126 | visitor.preVisit(obj) 127 | when INVISIT 128 | visitor.inVisit(obj) 129 | when POSTVISIT 130 | visitor.postVisit(obj) 131 | end 132 | end 133 | end 134 | 135 | def breadthFirstTraversal 136 | queue = QueueAsLinkedList.new 137 | queue.enqueue(self) if not empty? 138 | while not queue.empty? 139 | head = queue.dequeue 140 | yield head.key 141 | for i in 0 ... head.degree 142 | child = head.getSubtree(i) 143 | queue.enqueue(child) if not child.empty? 144 | end 145 | end 146 | end 147 | 148 | def breadthFirstTraversalAccept(visitor) 149 | breadthFirstTraversal do |obj| 150 | break if visitor.done? 151 | visitor.visit(obj) 152 | end 153 | end 154 | 155 | def each(&block) 156 | depthFirstTraversal do |obj, mode| 157 | block.call(obj) if mode == PREVISIT 158 | end 159 | end 160 | 161 | 162 | class Iterator < Opus8::Iterator 163 | 164 | def initialize(tree) 165 | @stack = StackAsLinkedList.new 166 | @stack.push(tree) if not tree.empty? 167 | end 168 | 169 | def more? 170 | not @stack.empty? 171 | end 172 | 173 | def succ 174 | if more? 175 | top = @stack.pop 176 | i = top.degree - 1 177 | while i >= 0 178 | subtree = top.getSubtree(i) 179 | @stack.push(subtree) if not subtree.empty? 180 | i -= 1 181 | end 182 | result = top.key 183 | else 184 | result = nil 185 | end 186 | return result 187 | end 188 | 189 | end 190 | 191 | def iter 192 | Iterator.new(self) 193 | end 194 | 195 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 编程基础 - Ruby示例 2 | 3 | 这是一个有着程序示例的知识库,且附带描述当下的编程规则与编程模式。 4 | 5 | #### 内容: 6 | - [线程](#线程) 7 | - [绿色线程](#绿色线程) 8 | - [全局解释器锁](#全局解释器锁) 9 | - [互斥锁](#互斥锁) 10 | - [纤程](#纤程) 11 | - [Rails的线程安全](#rails的线程安全) 12 | - [SOLID原则](#solid原则) 13 | - [单一职责原则](#单一职责原则) 14 | - [开放封闭原则](#开放封闭原则) 15 | - [里氏替换原则](#里氏替换原则) 16 | - [接口隔离原则](#接口隔离原则) 17 | - [依赖反转原则](#依赖反转原则) 18 | - [设计模式](#设计模式) 19 | - [创建型模式](#创建型模式) 20 | - [抽象工厂模式](#抽象工厂模式) 21 | - [生成器模式](#生成器模式) 22 | - [工厂模式](#工厂模式) 23 | - [原型模式](#原型模式) 24 | - [单例模式](#单例模式) 25 | - [结构型模式](#结构型模式) 26 | - [适配器模式](#适配器模式) 27 | - [组合模式](#组合模式) 28 | - [修饰模式](#修饰模式) 29 | - [外观模式](#外观模式) 30 | - [享元模式](#享元模式) 31 | - [代理模式](#代理模式) 32 | - [行为型模式](#行为型模式) 33 | - [责任链模式](#责任链模式) 34 | - [命令模式](#命令模式) 35 | - [解释器模式](#解释器模式) 36 | - [迭代器模式](#迭代器模式) 37 | - [中介者模式](#中介者模式) 38 | - [备忘录模式](#备忘录模式) 39 | - [观察者模式](#观察者模式) 40 | - [状态模式](#状态模式) 41 | - [策略模式](#策略模式) 42 | - [模板方法模式](#模板方法模式) 43 | - [访问者模式](#访问者模式) 44 | - [数据结构](#数据结构) 45 | - [数据结构的基本公理](#数据结构的基本公理) 46 | - [大O记号](#大o记号) 47 | - [实现](#实现) 48 | - [栈](#栈) 49 | - [队列](#队列) 50 | - [双端队列](#双端队列) 51 | - [单向链表](#单向链表) 52 | - [双向链表](#双向链表) 53 | - [有序列表](#有序列表) 54 | - [哈希表](#哈希表) 55 | - [二叉树](#二叉树) 56 | - [二叉查找树](#二叉查找树) 57 | - [B树](#B树) 58 | - [二叉堆](#二叉堆) 59 | - [算法](#算法) 60 | - [排序](#排序) 61 | - [冒泡排序](#冒泡排序) 62 | - [插入排序](#插入排序) 63 | - [选择排序](#选择排序) 64 | - [希尔排序](#希尔排序) 65 | - [堆排序](#堆排序) 66 | - [归并排序](#归并排序) 67 | - [快速排序](#快速排序) 68 | - [搜索](#搜索) 69 | - [二分查找](#二分查找) 70 | - [KMP查找](#kmp查找) 71 | 72 | ## 线程 73 | 74 | 需注意并行性和并发性:两者主要区别在于操作内存是使用进程还是线程。进一步说,进程会复制一份内存,而线程会分享内存。 这使得进程的产生比线程产生得慢,并且导致了进程一旦运行会消耗更多的资源。总的来说,线程相比进程引发的费用少。这个线程API是一个Ruby的API。我已经暗示了不同的Ruby实现有着不同的潜在线程行为。 75 | 76 | #### 绿色线程 77 | 78 | Ruby 1.9 用本地线程替换掉了绿色线程。然而,全局解释器锁依然妨碍了并行性。话虽如此,并发已通过更好的调度改进来获得了提升。 新的调度器通过将“上下文切换决策”实质上地弄为单独的本地线程(称为定时器线程),使其更加有效率。 79 | 80 | #### 全局解释器锁 81 | 82 | MRI有一个围绕着Ruby代码执行的全局解释器锁(GIL)。意味着在多线程上下文中,在任意时刻只有一个线程可以执行Ruby代码。 83 | 84 | 因此如果在一个8核的机器上你有8个线程在忙碌着,在任何给定的时间只有一个线程核一个核在工作。竞争冒险会毁坏数据,GIL的存在是为了保护Ruby内部不受其影响。虽说GIL有很多警告和优化,但是这才是要旨。 85 | 86 | 这对于MRI有着很多非常重要的影响,最大的影响在于GIL阻止了Ruby代码在MRI上并行运行。 87 | 88 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/threads/gil.rb) 89 | 90 | #### 互斥锁 91 | 92 | 互斥锁给多线程能同步访问代码中的重要部分提供了一种机制。换句话说,互斥锁给多线程的混乱世界带了秩序和保证。 93 | 94 | Mutex是mutual exclusion(相互排斥)的缩写。如果你把你的代码用互斥锁括起来,就能保证没有两个线程能在同一时刻进入那段代码。 95 | 96 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/threads/mutex.rb) 97 | 98 | #### 纤程 99 | 100 | 在Ruby中,纤程是实现轻量协作并发的基元。基本上,纤程是一套创造可暂停、恢复的代码块的手段。主要的差异在于纤程从不被强占,调度器只能由程序员完成而不是VM。与其他无堆栈轻量级并发模型不同,每个纤程都有一个小小的4KB堆栈。这使得纤程能够在纤程块内被深度嵌套的函数调用暂停。 101 | 102 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/threads/fibers.rb) 103 | 104 | #### Rails的线程安全 105 | 106 | Rails的线程安全问题在于没有一种简单的方式能绝对确定一个应用程序作为一个整体是线程安全的。 107 | 108 | * 全局变量是全局的,这意味着在线程种他们是分享的。到目前为止,如果你不能确信是否要使用全局变量,这有着其它原因来说明不要去触碰它。如果你真的需要在一个程序应用中分享一些全局内容,不管怎样,你最好还是用常量来伺候(往下看)。 109 | 110 | * 类变量,对于讨论线程的目的,类变量和全局变量没多大区别。类变量一样是在线程中进行分享。问题不在于使用类变量,而是在于改变他们。如果你不准备改变类变量,大多数情况下常量是一个更好的选择。 111 | 112 | * 实例变量,在Ruby中,或许你已经了解到你应该总是使用实例变量而不是类变量。好吧,或许你应该如此,但是和类变量一样,他们在线程程序中也是有问题的。 113 | 114 | 115 | * 记忆化函数本身不是一个线程安全的问题,然而,由于一些原因它很容易成为一个问题: 116 | 117 | - 它时常习惯于在类变量或者实例变量中存储数据(看前面) 118 | - ||=操作符实际上是两个操作,所以在其中可能存在上下文切换,造成线程间的竞争冒险。 119 | - 很容易忽略记忆化函数也能造成线程安全问题,并且告诉人们只需要避免类变量和实例变量。然后,问题比那个复杂。 120 | - 在这个线程安全问题上,Evan Phoenix在Rails代码库种挤入了一个非常棘手的竞争冒险错误,这个错误是在记忆化函数调用上一层出现的。所以即便你只使用实例变量,你也会在记忆化函数中以竞争冒险结束。 121 | 122 | 确保记忆化在你的例子中函数有意义、起作用。在很多例子中,不管怎样,Rails实际上缓存了结果,所以你不是用记忆化方法存了一整个资源。不要记忆类变量或者实例变量。如果你需要记忆类一级别的东西,用线程中的局部变量 (Thread.current[:baz])即可。注意,虽然如此,那也是某种意义上的全局变量。所以虽然这是线程安全的,但仍旧不是良好的编程实践。 123 | 124 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/threads/rails.rb) 125 | 126 | #### 代码及文献资源: 127 | 128 | * [http://www.jstorimer.com/blogs/workingwithcode/8085491-nobody-understands-the-gil](http://www.jstorimer.com/blogs/workingwithcode/8085491-nobody-understands-the-gil) 129 | 130 | * [https://en.wikipedia.org/wiki/Global_interpreter_lock](https://en.wikipedia.org/wiki/Global_interpreter_lock) 131 | 132 | * [http://www.csinaction.com/2014/10/10/multithreading-in-the-mri-ruby-interpreter/](http://www.csinaction.com/2014/10/10/multithreading-in-the-mri-ruby-interpreter/) 133 | 134 | ## SOLID原则 135 | 136 | 在计算机编程领域,SOLID原则是一个由Michael Feathers介绍引入、由Robert C. Martin在21世纪早期命名的“五个首要原则”的记忆术首字母缩写词,其代表了五个关于面向对象编程与设计的五个基本原则,包括单一职责、开闭原则、里氏替换、接口隔离以及依赖反转。这些原则的目的在于,当他们一起应用时,将会使得程序员更容易创造一个容易维护和随时间扩展的系统。SOLID的原则是可以应用于在编写软件过程中去除代码异味的指导原则,通过程序员重构软件的源代码,直到它是清晰可读的和可扩展的。它是软件敏捷、自适应开发的全部战略中的一部分。 137 | 138 | #### 单一职责原则 139 | 140 | 一个类应该只有一个职责。 141 | 142 | 每个类应该只有一个职责,并且这一职责应该完全封装起来。 143 | 它的所有服务应该与这一责任紧密相连,这包括了高凝聚。 144 | 145 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/solid/single_responsibility.rb) 146 | 147 | #### 开放封闭原则 148 | 149 | 软件实体应该为了扩展而开放,但是为了修改而关闭。 150 | 也就是说,这样的实体可以允许其行为被扩展而不修改其源代码。 151 | 152 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/solid/open_close.rb) 153 | 154 | #### 里氏替换原则 155 | Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program. 156 | 157 | It states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.) 158 | 159 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/solid/liskov_substitution.rb) 160 | 161 | #### 接口隔离原则 162 | 163 | 多个客户端特定接口比单一通用接口更好。 164 | 165 | 任何客户都不应该被迫依赖于它不使用的方法。 166 | ISP将非常大的接口分割成更小和更具体的接口,使得客户端将只需要知道他们感兴趣的方法。 这种缩小的接口也称为角色接口。 167 | ISP旨在保持系统解耦,从而更容易重构,更改和重新部署。 168 | 169 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/solid/interface_segregation.rb) 170 | 171 | ####依赖反转原则 172 | 173 | 应该依赖于抽象,而非依赖于具体的结构。 174 | 175 | 指的是将软件模块去耦的特定形式。 当遵循该原理时,从高级策略设置模块到低级,依赖性模块建立的常规依赖关系被反转(即颠倒),从而使高级模块独立于低级模块实现细节。 176 | 177 | ####代码及文献资源: 178 | 179 | * [https://gist.github.com/khusnetdinov/9d8f50fdcaab197871b31578f2e14d5d](https://gist.github.com/khusnetdinov/9d8f50fdcaab197871b31578f2e14d5d) 180 | 181 | * [https://robots.thoughtbot.com/back-to-basics-solid](https://robots.thoughtbot.com/back-to-basics-solid) 182 | 183 | * [https://subvisual.co/blog/posts/19-solid-principles-in-ruby](https://subvisual.co/blog/posts/19-solid-principles-in-ruby) 184 | 185 | * [http://blog.siyelo.com/solid-principles-in-ruby/](http://blog.siyelo.com/solid-principles-in-ruby/) 186 | 187 | ## 设计模式 188 | 189 | ### 创建型模式 190 | 191 | 在软件工程中,创建型设计模式是处理对象创建机制的设计模式,试图以适应情势的方式创建对象。 对象创建的基本形式可能导致设计问题或增加设计的复杂性。 创建型设计模式通过某种方式控制这个对象创建来解决这个问题。 创建型设计模式由两个主导思想组成。 一个是封装关于系统使用哪个具体类的知识。 另一个是隐藏这些具体类的实例是如何创建和组合的。 192 | 193 | [维基百科](https://en.wikipedia.org/wiki/Creational_pattern) 194 | 195 | #### 抽象工厂模式 196 | 197 | 抽象工厂模式提供了一种方式来封装一组具有共同主题的个体工厂,而不指定他们的具体类。 在正常使用中,客户端软件创建抽象工厂的具体实现,然后使用工厂的通用接口创建作为主题一部分的具体对象。 客户端不知道(或关心)具体对象从哪个内部工厂获得,因为它仅使用其产品的通用接口。 此模式将一组对象的实现细节与其一般用法分离,并依赖对象组合,因为对象创建是在工厂接口中公开的方法中实现的。 198 | 199 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/creational/abstract_factory.rb) | [维基百科](https://en.wikipedia.org/wiki/Abstract_factory_pattern) 200 | 201 | #### 生成器模式 202 | 203 | 生成器模式是一种对象创建型软件设计模式。 不像抽象工厂模式和工厂方法模式的意图是启用多态性,生成器模式的意图是找到一个解决方案的伸缩构造函数反模式。 当对象构造函数参数组合的增加导致构造函数的指数级列表时,出现了伸缩构造函数反模式。 生成器模式使用另一个对象,即生成器,而不是使用大量构造函数,而是逐步接收每个初始化参数,然后一次返回生成的构造对象。 204 | 205 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/creational/builder.rb) | [维基百科](https://en.wikipedia.org/wiki/Builder_pattern) 206 | 207 | #### 工厂模式 208 | 209 | 在基于类的编程中,工厂方法模式是一种创建型模式,它使用工厂方法来处理创建对象的问题,而无需指定将要创建对象的具体类。 这是通过调用工厂方法(在接口中指定并由子类实现,或在基类中实现,并随意地由派生类覆盖而不是通过调用构造函数)创建对象来完成的。 210 | 211 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/creational/factory.rb) | [维基百科](https://en.wikipedia.org/wiki/Factory_method_pattern) 212 | 213 | #### 原型模式 214 | 215 | 原型模式是软件开发中的创建设计模式。 当要创建的对象类型由原型实例确定时使用,该原型实例被克隆以生成新对象。 此模式用于: 216 | 217 | * 在客户端应用程序中消除对象创建者的子类,类似于抽象工厂模式的作法。 218 | * 当给定应用程序过于昂贵时,避免以标准方式创建新对象(例如,使用“new”关键字)的固有成本。 219 | 220 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/creational/prototype.rb) | [维基百科](https://en.wikipedia.org/wiki/Prototype_pattern) 221 | 222 | #### 单例模式 223 | 224 | 确保一个类只有一个实例,并提供一个访问它的全局指针。 当需要一个对象来协调系统上的操作时,这是很有用的。 该概念有时被推广到仅有一个对象能更有效操作,或者对象实例化限制到一定数量的系统。 225 | 226 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/creational/singleton.rb) | [维基百科](https://en.wikipedia.org/wiki/Singleton_pattern) 227 | 228 | #### 其它模式: 229 | 230 | * [惰性初始模式](https://en.wikipedia.org/wiki/Lazy_initialization) 231 | 232 | * [多例模式](https://en.wikipedia.org/wiki/Multiton_pattern) 233 | 234 | * [对象池](https://en.wikipedia.org/wiki/Object_pool_pattern) 235 | 236 | * [RAII](https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) 237 | 238 | * [效用模式](https://en.wikipedia.org/wiki/Utility_pattern) 239 | 240 | ### 结构型模式 241 | 242 | 在软件工程中,结构设计模式通过确定一种简单方式来认识实体之间的关系,从而简化设计的设计模式。 243 | 244 | [维基百科](https://en.wikipedia.org/wiki/Structural_pattern) 245 | 246 | #### 适配器模式 247 | 248 | 在软件工程中,适配器模式是一种允许现有类的接口用作另一个接口的软件设计模式。 它通常用于使现有类与其他类一起工作而不用不修改其源代码。 249 | 250 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/structural/adapter.rb) | [维基百科](https://en.wikipedia.org/wiki/Adapter_pattern) 251 | 252 | #### 组合模式 253 | 254 | 组合模式是一种用于表示具有分层树结构的对象的结构模式。 它允许单个叶节点和由许多节点组成的分支的统一处理。 255 | 256 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/structural/composite.rb) | [维基百科](https://en.wikipedia.org/wiki/Composite_pattern) 257 | 258 | #### 修饰模式 259 | 260 | 在面向对象编程中,修饰模式(也称为Wrapper,适配器模式的替代命名)是一种设计模式,允许将静态行为或动态行为添加到单个对象,而不影响同一个类的其他对象。修饰模式通常对遵守单一责任原则很有用,因为它允许功能被划分到具有唯一关注领域的类之中。 261 | 262 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/structural/decorator.rb) | [维基百科](https://en.wikipedia.org/wiki/Decorator_pattern) 263 | 264 | #### 外观模式 265 | 266 | 当系统具有大量相互依赖的类或其源代码不可用以至于非常复杂或难以理解时,通常使用外观设计模式。 这种模式隐藏了较大系统的复杂性,并为客户端提供了一个更简单的接口。它通常有一个包含客户端所需的一组成员的单个包装类。 这些成员代表外观客户端访问系统并隐藏实现细节。 267 | 268 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/structural/facade.rb) | [维基百科](https://en.wikipedia.org/wiki/Facade_pattern) 269 | 270 | #### 享元模式 271 | 272 | 在计算机编程中,享元模式是一种软件设计模式。享元模式是一个对象,通过与其他类似对象共享尽可能多的数据来最小化内存使用;它是一种大量使用对象的方法,用于当简单且重复的指代将使用巨量内存时。 通常,对象状态的某些部分是可以共享的,并且通常的做法是将它们保存在外部数据结构中,并在使用时将它们临时传递给享元对象。 273 | 274 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/structural/flyweight.rb) | [维基百科](https://en.wikipedia.org/wiki/Flyweight_pattern) 275 | 276 | #### 代理模式 277 | 278 | 代理,最普遍的形式是一个类,作为其他东西的接口。 代理可以做任何东西的接口:网络连接,存储器中的大对象,文件或一些昂贵或不可能复制的其他资源。 简而言之,代理是一个包装器或代理对象,客户端调用它来访问幕后的实际服务对象。 代理的使用可以简单地转发到实际对象,或者可以提供附加逻辑。 在代理中,可以提供额外的功能,例如当实际对象进行资源密集型操作时提供高速缓存,或者在调用实际对象的操作之前检查前提条件。 对于客户端,代理对象的用法类似于使用实际对象,因为两者都实现相同的接口。 279 | 280 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/structural/proxy.rb) | [维基百科](https://en.wikipedia.org/wiki/Proxy_pattern) 281 | 282 | #### 其它模式: 283 | 284 | * [注释回调](http://c2.com/cgi/wiki?AnnotatedCallback) 285 | 286 | * [桥接模式](https://en.wikipedia.org/wiki/Bridge_pattern) 287 | 288 | * [数据总线模式](http://c2.com/cgi/wiki?DataBusPattern) 289 | 290 | * [角色对象模式](http://c2.com/cgi/wiki?RoleObjectPattern) 291 | 292 | ### 行为型模式 293 | 294 | 在软件工程中,行为型模式是识别对象之间的公共通信模式并实现这些模式的设计模式。 通过这样做,这些模式增加了执行该通信的灵活性。 295 | 296 | [维基百科](https://en.wikipedia.org/wiki/Behavioral_pattern) 297 | 298 | #### 责任链模式 299 | 300 | 在面向对象设计中,责任链模式是由命令对象源和一系列处理对象组成的设计模式。 每个处理对象包含的逻辑,定义了其可以处理的命令对象的类型;其余的被传递到链中的下一个处理对象。 还存在一个将新的处理对象添加到该链的末端的机制。 301 | 302 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/chain_of_responsibility.rb) | [维基百科](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) 303 | 304 | #### 命令模式 305 | 306 | 将请求封装为对象,从而允许具有不同请求的客户端的参数化,以及请求的排队或日志记录。 它还允许支持可撤销操作。 307 | 308 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/command.rb) | [维基百科](https://en.wikipedia.org/wiki/Command_pattern) 309 | 310 | #### 解释器模式 311 | 312 | 在计算机编程中,解释器模式是说明如何评估语言中的句子的设计模式。 基本思想是在专门的计算机语言中为每个符号(终端或非终端)设置一个类。 语言中的句子的语法树是复合模式的实例,并且用于为客户端评估(解释)句子。 313 | 314 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/interpreter.rb) | [维基百科](https://en.wikipedia.org/wiki/Interpreter_pattern) 315 | 316 | #### 迭代器模式 317 | 318 | 迭代器设计模式提供对容器中的元素的顺序访问,而不暴露容器如何具体地表示元素。 迭代器可以被认为是一个可移动的指针,允许访问封装在容器中的元素。 319 | 320 | *外部迭代器:迭代逻辑包含在一个单独的类中。 迭代类可以被概括为处理多个对象类型,只要它们允许索引。 虽然它需要附加类来做实际的迭代,但它们允许更大的灵活性,因为你可以控制迭代及迭代顺序。 321 | 322 | *内部迭代器:所有迭代逻辑发生在聚合对象内部。 使用代码块将你的逻辑传递给聚合,然后为每个元素调用代码块。 323 | 324 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/iterator.rb) | [维基百科](https://en.wikipedia.org/wiki/Iterator_pattern) 325 | 326 | #### 中介者模式 327 | 328 | 通常一个程序是由大量的类组成的,所以逻辑和计算分布在这些类中。 然而,随着在程序中开发更多的类,特别是在维护或重构期间,这些类之间的通信问题可能变得更复杂。这使得程序更难以阅读和维护。 此外,可能变得难以改变程序,因为任何改变可能影响其他几个类中的代码。 使用中介器模式,对象之间的通信用中介器对象封装。 对象彼此间不再直接通信,而是通过中介器进行通信。 这减少了通信对象之间的依赖性,从而降低了耦合。 329 | 330 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/mediator.rb) | [维基百科](https://en.wikipedia.org/wiki/Mediator_pattern) 331 | 332 | #### 备忘录模式 333 | 334 | 备忘录模式由三个对象实现:发起者,守护者备忘录。 发起者是具有内部状态的一些对象。 守护者将操作发起者,但希望能够撤消该更改。 守护者首先向发起人请求备忘录。 然后它将要做任何操作(或操作序列)。 要回滚到操作之前的状态,它将备忘录对象返回给发起者。 备忘录对象本身是一个不透明的对象(守护者不能,或不应该改变)。当使用这种模式时,请注意 - 如果发起者可能改变其他对象或资源,备忘录模式对单个对象进行操作。 335 | 336 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/momento.rb) | [维基百科](https://en.wikipedia.org/wiki/Memento_pattern) 337 | 338 | #### 观察者模式 339 | 340 | 观察者模式是一种软件设计模式,其中称为主题的对象维护其依赖项的列表,称为观察者,并且通常通过调用它们的方法来自动通知任何状态改变。 它主要用于实现分布式事件处理系统。 观察者模式也是熟悉的模型-视图-控制器(MVC)架构模式中的关键部分。观察者模式在许多编程库和系统中实现,包括几乎所有的GUI工具包。 341 | 342 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/observer.rb) | [维基百科](https://en.wikipedia.org/wiki/Observer_pattern) 343 | 344 | #### 状态模式 345 | 346 | 状态模式是以面向对象的方式实现状态机的行为型模式。 利用状态模式,通过将每个单独状态实现为状态模式接口的派生类,并通过调用由模式的超类定义的方法来实现状态转换,从而实现了状态机。 347 | 348 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/state.rb) | [维基百科](https://en.wikipedia.org/wiki/State_pattern) 349 | 350 | #### 策略模式 351 | 352 | 在计算机编程中,策略模式(也称为政策模式)是使得能够在运行时选择算法的软件设计模式。战略模式 353 | 354 | *定义一系列算法, 355 | *封装每个算法 356 | *使得算法在该系列中可互换。 357 | 358 | 策略模式允许算法变化独立于使用它的客户端。Gamma等人所著的一本非常有影响力的书“设计模式”普及了使用模式来描述软件设计的概念,策略模式是其中包含的模式之一。 359 | 360 | 例如,对输入数据执行验证的类可以使用策略模式来基于数据的类型、数据的源、用户选择或其他辨别因素来选择验证算法。直到运行时间,这些因素在每种情况下才是可知的,并且可能需要进行彻底不同的验证。与验证对象分开封装的验证策略,可以由系统的不同区域(或甚至不同系统)中的其他验证对象使用,而无需代码重复。 361 | 362 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/strategy.rb) | [维基百科](https://en.wikipedia.org/wiki/Strategy_pattern) 363 | 364 | #### 模板方法模式 365 | 366 | 在面向对象编程中,首先创建一个提供算法设计的基本步骤的类。 这些步骤使用抽象方法实现。 之后,子类改变了抽象方法来实现真正的操作。 因此通用算法保存在一个位置,但具体步骤可以由子类修改。 367 | 368 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/template_method.rb) | [维基百科](https://en.wikipedia.org/wiki/Template_method_pattern) 369 | 370 | #### 访问者模式 371 | 372 | 在面向对象的编程和软件工程中,访问者设计模式是将算法与其操作的对象结构分离的一种方式。这种分离的实际结果是在不修改这些结构的情况下向现有对象结构添加新操作的能力。它是遵循开放封闭原则的一种方式。 373 | 374 | 实质上,访问者模式允许向一系列类添加新的虚拟函数而不修改类本身,反而是创建一个访问者类来实现所有虚拟函数适当的特殊化。访问者将实例引用作为输入,并通过双调度实现目标。 375 | 376 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/patterns/behavioral/visitor.rb) | [维基百科](https://en.wikipedia.org/wiki/Visitor_pattern) 377 | 378 | #### 其它模式: 379 | 380 | * [访问者模式](http://c2.com/cgi/wiki?HierarchicalVisitorPattern) 381 | 382 | #### 代码及文献资源: 383 | 384 | * [https://ru.wikipedia.org/wiki/Design_Patterns](https://ru.wikipedia.org/wiki/Design_Patterns) 385 | 386 | * [https://en.wikipedia.org/wiki/Creational_pattern](https://en.wikipedia.org/wiki/Creational_pattern) 387 | 388 | * [https://en.wikipedia.org/wiki/Structural_pattern](https://en.wikipedia.org/wiki/Structural_pattern) 389 | 390 | * [https://en.wikipedia.org/wiki/Behavioral_pattern](https://en.wikipedia.org/wiki/Behavioral_pattern) 391 | 392 | * [http://c2.com/cgi/wiki?CategoryPattern](http://c2.com/cgi/wiki?CategoryPattern) 393 | 394 | * [https://gist.github.com/martindemello/7231bf0f407ca428b509](https://gist.github.com/martindemello/7231bf0f407ca428b509) 395 | 396 | ## 数据结构 397 | 398 | 在计算机科学中,大O符号用于通过算法如何响应输入大小的变化来对其进行分类,例如算法的处理时间随着问题规模变得非常大而变化。 在分析数论中,它用于估计“错误提交”,用一个大的有限自变量取代一个算术函数的渐进大小。 一个着名的例子是估计质数定理中的余项的问题。 399 | 400 | ### 数据结构的基本公理 401 | 402 | 公共语言的运行时间性能由我们现在假设的一组公理给出。 403 | 404 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/axioms.rb) 405 | 406 | #### 代码及文献资源: 407 | 408 | * [Axioms](http://www.brpreiss.com/books/opus8/html/page37.html) 409 | 410 | ### 大O记号 411 | 412 | * [Conventions for Writing Big Oh Expressions](http://www.brpreiss.com/books/opus8/html/page66.html) 413 | 414 | * [Rules For Big Oh Analysis of Running Time](http://www.brpreiss.com/books/opus8/html/page72.html) 415 | 416 | * [Reality Check](http://www.brpreiss.com/books/opus8/html/page76.html) 417 | 418 | * [Example: Ruby method with Big O explanation](http://www.brpreiss.com/books/opus8/html/page71.html) 419 | 420 | * [Example: Example-Prefix Sums](http://www.brpreiss.com/books/opus8/html/page73.html) 421 | 422 | * [Example: Bucket Sort](http://www.brpreiss.com/books/opus8/html/page75.html) 423 | 424 | ### 实现 425 | 426 | > NOTE: 所有数据结构都是作为学习目的的示例。 对于在项目中使用,请参考其他资源。 大多数例子取自Bruno R. Preiss的[网站](http://www.brpreiss.com/)。 427 | 428 | #### 栈 429 | 430 | 堆栈是队列的兄弟。 它模仿了现实生活中的堆栈(例如纸)。 它是“先进后出”FILO(first-in-last-out),因此当从堆栈中检索项目时,它们按照添加顺序的反向返回。 并且,Ruby数组提供了一个完美的容器。 与队列一样,它也可以用链表实现。 431 | 432 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 433 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 434 | [栈](http://en.wikipedia.org/wiki/Stack_(abstract_data_type)) | `Θ(n)` | `Θ(n)` | `Θ(1)` | `Θ(1)` | `O(n)` | `O(n)` | `O(1)` | `O(1)` | 435 | 436 | [栈(数组) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/stack_as_array.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page131.html) 437 | 438 | [栈(链表) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/stack_as_linked_list.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page137.html) 439 | 440 | #### 队列 441 | 442 | 队列是一个简单的基于容器的结构,模拟了现实生活中的队列(例如在银行排队等待)。 它是先进先出FIFO(first-in-first-out),意味着当您从队列中检索项目时,它们按照它们的添加顺序返回。 Ruby数组提供了使Queue实现简单易用的方法,但是让它们适当地命名并包含在一个方便的类中是值得去看看它们是如何实现的,并且因为其他结构将继承这个方法。队列可以使用链表来完成替代实现。 443 | 444 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 445 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 446 | [队列](http://en.wikipedia.org/wiki/Queue_(abstract_data_type)) | `Θ(n)` | `Θ(n)` | `Θ(1)` | `Θ(1)` | `O(n)` | `O(n)` | `O(1)` | `O(1)` | 447 | 448 | [队列(数组) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/queue_as_array.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page147.html) 449 | 450 | [队列(链表) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/queue_as_linked_list.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page151.html) 451 | 452 | #### 双端队列 453 | 454 | 双端队列是一个允许在两端添加和删除项目的队列。 455 | 456 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 457 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 458 | [双端队列](https://en.wikipedia.org/wiki/Double-ended_queue) | `Θ(n)` | `Θ(n)` | `Θ(1)` | `Θ(1)` | `O(n)` | `O(n)` | `O(1)` | `O(1)` | 459 | 460 | [双端队列(数组) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/deque_as_array.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page158.html) 461 | 462 | [双端队列(链表) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/deque_as_linked_list.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page161.html) 463 | 464 | #### 单向链表 465 | 466 | 单向链表的节点包含数据字段以及“下一个”域,其指向链表中的下一个节点。可以对单向链表执行的操作包括插入,删除和遍历。 467 | 468 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 469 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 470 | [单向链表](http://en.wikipedia.org/wiki/Singly_linked_list#Singly_linked_lists) | `Θ(n)` | `Θ(n)` | `Θ(1)` | `Θ(1)` | `O(n)` | `O(n)` | `O(1)` | `O(1)` | 471 | 472 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/singly_linked_list.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page96.html#SECTION004300000000000000000) | [维基百科](https://en.wikipedia.org/wiki/Linked_list#Singly_linked_list) 473 | 474 | #### 双向链表 475 | 476 | 在双向链表中,每个链表节点包含两个引用 - 一个指向其后继,一个指向其前任。 477 | 478 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 479 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 480 | [双向链表](http://en.wikipedia.org/wiki/Doubly_linked_list) | `Θ(n)` | `Θ(n)` | `Θ(1)` | `Θ(1)` | `O(n)` | `O(n)` | `O(1)` | `O(1)` | 481 | 482 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/double_linked_list.rb) 483 | 484 | #### 有序列表 485 | 486 | 有序列表是一种项目顺序非常重要的列表。但是,有序列表中的项目不一定排序。因此,可以改变项目的顺序,并且仍然具有有效的有序列表。 487 | 488 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 489 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 490 | 有序列表(数组) |`Θ(1)` | `Θ(n)` | `Θ(1)` | `Θ(1)` | `O(1)` | `O(n)` | `O(1)` | `O(1)` | 491 | 有序列表(链表) |`Θ(n)` | `Θ(n)` | `Θ(1)` | `Θ(1)` | `O(n)` | `O(n)` | `O(1)` | `O(1)` | 492 | 493 | [有序列表(数组) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/ordered_list_as_array.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page169.html) 494 | 495 | [有序列表(链表) - 示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/ordered_as_linked_list.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page178.html) 496 | 497 | #### 哈希表 498 | 499 | 哈希表是一种可搜索的容器。因此,它提供了用于插入对象、查找对象、删除对象的方法。 500 | 501 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 502 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 503 | [哈希表](http://en.wikipedia.org/wiki/Hash_table) | `Θ(n)` | `Θ(1)` | `Θ(1)` | `Θ(1)` | `Θ(n)` | `O(n)` | `O(n)` | `O(n)` | 504 | 505 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/hash_table.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page222.html) 506 | 507 | #### 二叉树 508 | 509 | 二叉树是每个节点最多可以有两个子节点的树。孩子们被指定为左节点和右节点。 510 | 511 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 512 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 513 | [二叉树](https://en.wikipedia.org/wiki/Binary_tree) | `Θ(log(n))` | `Θ(log(n))` | `Θ(log(n))` | `Θ(log(n))` | `O(n)` | `O(n)` | `O(n)` | `O(n)` | 514 | 515 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/binary_tree.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page286.html) 516 | 517 | #### 二叉查找树 518 | 519 | 在计算机科学中,二叉查找树(BST)(有时称为有序二叉树或排序二叉树)是特定类型的容器:在内存中存储“项目”(例如数字,名称等)的数据结构。 它们允许快速查找,添加和删除项目,并且可以用于实现项目的动态集合或者允许通过查找其键来查找项目(例如,通过名称找到人的电话号码)。 520 | 521 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 522 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 523 | [二叉查找树](http://en.wikipedia.org/wiki/Binary_search_tree) | `Θ(log(n))` | `Θ(log(n))` | `Θ(log(n))` | `Θ(log(n))` | `O(n)` | `O(n)` | `O(n)` | `O(n)` | 524 | 525 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/binary_search_tree.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page300.html) 526 | 527 | #### B树 528 | 529 | 在计算机科学中,B树是自平衡树数据结构,其保持数据有序并允许在对数时间内的搜索,顺序存取,插入和删除。 B树是二叉查找树的概括,因为一个节点可以有多于两个子节点(与自平衡二叉搜索树不同,B树是针对读取和写入大块数据的系统进行优化的。 B树是提供给外存的数据结构的一个很好的例子,它常用于数据库和文件系统。 530 | 531 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 532 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 533 | [B树](http://en.wikipedia.org/wiki/B_tree) | `Θ(log(n))` | `Θ(log(n))` | `Θ(log(n))` | `O(log(n))` | `O(log(n))` | `O(log(n))` | `O(log(n))` | `O(log(n))` | 534 | 535 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/b_tree.rb) | [实现步骤](http://www.brpreiss.com/books/opus8/html/page339.html) 536 | 537 | #### 二叉堆 538 | 539 | 二叉堆是使用数组实现的堆有序完整二叉树。 在堆中,最小的键在根处找到,并且由于根总是在数组的第一个位置找到,因此找到最小的键是二进制堆中的一个微不足道的操作。 540 | 541 | | 数据结构 | 访问平均 | 查找平均 | 插入平均 | 删除平均 | 访问最坏 | 查找最坏 | 插入最坏 | 删除最坏 | 542 | |-----------|---------------:|---------------:|------------------:|-----------------:|-------------:|-------------:|----------------:|---------------:| 543 | [二叉堆](https://en.wikipedia.org/wiki/Binary_heap) | `Θ(n)` | `Θ(n)` | `Θ(1)` | `O(log(n))` | `O(n)` | `O(n))` | `O(log(n))` | `O(log(n))` | 544 | 545 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/structures/binary_heap.rb) | [实现步骤] (http://www.brpreiss.com/books/opus8/html/page355.html) 546 | 547 | #### 代码及文献资源: 548 | 549 | * [https://github.com/blahah/datastructures](https://github.com/blahah/datastructures) 550 | 551 | * [http://www.brpreiss.com](http://www.brpreiss.com) 552 | 553 | * [https://en.wikipedia.org/wiki/List_of_data_structures](https://en.wikipedia.org/wiki/List_of_data_structures) 554 | 555 | * [https://www.cs.cmu.edu/~adamchik/15-121/lectures/Trees/trees.html](https://www.cs.cmu.edu/~adamchik/15-121/lectures/Trees/trees.html) 556 | 557 | * [http://bigocheatsheet.com/](http://bigocheatsheet.com/) 558 | 559 | * [https://gist.github.com/TSiege/cbb0507082bb18ff7e4b](https://gist.github.com/TSiege/cbb0507082bb18ff7e4b) 560 | 561 | ## 算法 562 | 563 | ### 排序 564 | 565 | 排序算法是以一定顺序排列列表元素的算法。 最常用的是数字顺序和字典顺序。 高效排序对于优化其他算法(例如搜索和合并算法)的使用是重要的,这些算法要求输入数据在排序列表中; 它也常用于规范化数据和产生人类可读的输出。 566 | 567 | #### 冒泡排序 568 | 569 | 冒泡排序和插入排序有许多相同的属性,但有稍高的开销。 在几乎有序的数据的情况下,冒泡排序需要O(n)时间,但是需要至少遍历2遍数据(而插入排序需要差不多1遍)。 570 | 571 | | 最好 | 平均 | 最坏 | 572 | |-----:|--------:|------:| 573 | | `Ω(n)` | `Θ(n^2)` | `O(n^2)` | 574 | 575 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/bubble.rb) | [维基百科](https://en.wikipedia.org/wiki/Bubble_sort) 576 | 577 | #### 插入排序 578 | 579 | 虽然它是具有O(n2)最坏情况时间的基本排序算法之一,但是当数据几乎被排序(因为它是自适应的)时或者当问题规模小时(因为它低开销),插入排序是可以选择的算法。 580 | 由于这些原因,并且因为它也是稳定的,所以插入排序通常用于更高开销的分治算法的递归基础(当问题规模较小时),例如合并排序或快速排序。 581 | 582 | | 最好 | 平均 | 最坏 | 583 | |-----:|--------:|------:| 584 | | `Ω(n)` | `Θ(n^2)` | `O(n^2)` | 585 | 586 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/insertion.rb) | [维基百科](https://en.wikipedia.org/wiki/Insertion_sort) 587 | 588 | #### 选择排序 589 | 590 | 从这里提供的比较,可能会得出结论,不应该使用选择排序。 它不适合任何排列方式的数据(注意上面的四个图表),它的运行时总是二次的。 591 | 但是,选择排序具有最小化交换数量的属性。 在交换项目的成本高的应用中,可能选择使用选择排序。 592 | 593 | | 最好 | 平均 | 最坏 | 594 | |-----:|--------:|------:| 595 | | `Ω(n^2)` | `Θ(n^2)` | `O(n^2)` | 596 | 597 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/selection.rb) | [维基百科](https://en.wikipedia.org/wiki/Selection_sort) 598 | 599 | #### 希尔排序 600 | 601 | 希尔排序的最糟糕的时间复杂度取决于增量序列。 对于这里使用的增量1 4 13 40 121 ...,时间复杂度是O(n3 / 2)。 对于其他增量,已知时间复杂度为O(n4 / 3)和甚至O(n·lg2(n))。 时间复杂度的上限和最佳增量序列都是未知的。 602 | 因为希尔排序基于插入排序,所以shell排序继承了插入排序的自适应属性。自适应属性不那么显着,因为希尔排序需要遍历一遍每次递增的数据的,但它是重要的。 对于上述增量序列,有log3(n)个增量,因此几乎排序的数据的时间复杂度为O(n·log3(n))。 603 | 由于其低开销,相对简单的实现,自适应属性和子二次时间复杂度,对于某些应用程序,当需要排序的数据不是很大的时候,希尔排序可能是O(n·lg(n))排序算法的可行替代 。 604 | 605 | | 最好 | 平均 | 最坏 | 606 | |-----:|--------:|------:| 607 | | `Ω(n log(n))` | `Θ(n(log(n))^2)` | `O(n(log(n))^2)` | 608 | 609 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/shell.rb) | [维基百科](https://en.wikipedia.org/wiki/Shellsort) 610 | 611 | #### 堆排序 612 | 613 | 堆排序易于实现,执行O(n·lg(n))就地排序,但不稳定。 614 | 第一个循环,Θ(n)堆化阶段,将数组放入堆。 第二个循环,O(n·lg(n))排序阶段,重复提取最大值和修复堆顺序。 615 | 为了清楚起见,递归地写下沉功能。 因此,如图所示,代码需要用于递归调用栈的Θ(lg(n))空间。 然而,下沉中的尾递归很容易转换为迭代,这产生了O(1)空间束缚。 616 | 两个阶段略微自适应,虽然不是以任何特别有用的方式。 在几乎有序的情况下,堆化阶段销毁原始顺序。 在相反的情况下,堆化阶段是尽可能快的,因为数组以堆顺序开始,但在排序阶段是一贯的。 在少数独特的关键字案例中,有一些加速,但不如希尔排序或3路快速排序。 617 | 618 | | 最好 | 平均 | 最坏 | 619 | |-----:|--------:|------:| 620 | | `Ω(n log(n))` | `Θ(n log(n))` | `O(n log(n))` | 621 | 622 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/heap.rb) | [维基百科](https://en.wikipedia.org/wiki/Heapsort) 623 | 624 | #### 归并排序 625 | 626 | 归并排序是十分可预测的。 它使得每个元素进行0.5lg(n)到lg(n)次比较,以及每个元素交换lg(n)到1.5lg(n)次。 对已经排序的数据实现最小比较和交换; 平均来说,对于随机数据实现最多比较和交换。 如果不担心使用Θ(n)额外空间,那么归并排序是一个很好的选择:它很容易实现,它是唯一稳定的O(n·lg(n))排序算法。 注意,当排序链表时,归并排序只需要Θ(lg(n))额外空间(用于递归)。 627 | 归并排序是针对各种情况都可选择的算法:需要稳定性时,排序链表时,以及当随机访问比顺序访问贵得多时(例如,磁带上的外部排序)。 628 | 对于算法的最后一步,存在线性时间就地归并算法,但是它们既昂贵又复杂。 当Θ(n)额外空间不可用时,这个复杂度还是合理的。 629 | 630 | | 最好 | 平均 | 最坏 | 631 | |-----:|--------:|------:| 632 | | `Ω(n log(n))` | `Θ(n log(n))` | `O(n log(n))` | 633 | 634 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/merge.rb) | [维基百科](https://en.wikipedia.org/wiki/Merge_sort) 635 | 636 | #### 快速排序 637 | 638 | 当细心实现时,快速排序是健壮的并且具有低开销。当不需要稳定的排序时,快速排序是一个很好的通用排序 - 虽然应该总是使用3路分区版本。 639 | 上面显示的双路分区代码是为了清楚而不是最佳性能而编写的;它局部表现差,并且,当关键字很少花费O(n2)时间。Robert Sedgewick和Jon Bentley的论文Quicksort is Optimal给出了更有效和鲁棒的双向分割方法。当存在许多值等于中枢值时,鲁棒分区产生平衡递归,为所有输入产生O(n·lg(n))时间和O(lg(n))空间的概率保证。 640 | 对于递归执行的两个子排序,快速排序在递归不平衡的最坏情况下需要O(n)额外的空间用于递归堆栈。这是极不可能发生的,但它可以通过递归排序较小的子数组避免;第二个子数组排序是一个尾递归调用,可以通过迭代来完成。通过这种优化,算法在最坏的情况下使用O(lg(n))额外空间。 641 | 642 | | 最好 | 平均 | 最坏 | 643 | |-----:|--------:|------:| 644 | | `Ω(n log(n))` | `Θ(n log(n))` | `O(n^2)` | 645 | 646 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/quick.rb) | [维基百科](https://en.wikipedia.org/wiki/Quicksort) 647 | 648 | #### 其它排序算法 649 | 650 | * [http://rosettacode.org/wiki/Category:Sorting_Algorithms](http://rosettacode.org/wiki/Category:Sorting_Algorithms) 651 | 652 | * [https://www.toptal.com/developers/sorting-algorithms](https://www.toptal.com/developers/sorting-algorithms) 653 | 654 | #### Additional reading 655 | 656 | * [https://www.igvita.com/2009/03/26/ruby-algorithms-sorting-trie-heaps/](https://www.igvita.com/2009/03/26/ruby-algorithms-sorting-trie-heaps/) 657 | 658 | ### 搜索 659 | 660 | #### 二分查找 661 | 662 | 在计算机科学中,二分搜索,也称为半间隔搜索或对数搜索,是一种搜索算法,其在排序的数组中找到目标值的位置。 它将目标值与数组的中间元素进行比较; 如果它们不相等,则目标值不可能呆在的一半被排序,并且搜索在剩余的一半继续,直到它成功。 663 | 664 | | 最好 | 平均 | 最坏 | 665 | |-----:|--------:|------:| 666 | | `Θ(1)` | `Θ(n log(n))` | `O(log(n))` | 667 | 668 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/search/binary.rb) | [维基百科](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) 669 | 670 | #### KMP查找 671 | 672 | 在计算机科学中,Knuth-Morris-Pratt字符串搜索算法(或KMP算法)通过采用以下观察来搜索主“文本串”S内的“字”W的出现:当出现不匹配时,该字本身体现足够信息以确定下一个匹配可以开始的位置,从而绕过对先前匹配的字符的重新检查。 673 | 674 | [示例](https://github.com/fanjieqi/ruby.fundamental/blob/master/algorithms/sort/knuth-morris-pratt.rb) | [维基百科](https://en.wikipedia.org/wiki/Binary_search_algorithm) 675 | 676 | #### 其它算法 677 | 678 | * [Dijkstra算法](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) 679 | 680 | * [Kruskal算法](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm) 681 | 682 | * [最长上升子序列](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) 683 | 684 | * [电话号码对应英语单词](http://www.mobilefish.com/services/phonenumber_words/phonenumber_words.php) 685 | 686 | #### 代码及文献资源: 687 | 688 | * [https://github.com/sagivo/algorithms](https://github.com/sagivo/algorithms) 689 | 690 | * [https://github.com/kanwei/algorithms](https://github.com/kanwei/algorithms) 691 | 692 | 693 | 694 | --------------------------------------------------------------------------------