day-02.rb 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. ###########
  2. # Symbols #
  3. ###########
  4. # prefix by a colon
  5. :a
  6. :b
  7. :utf
  8. :anything
  9. # Hashtables
  10. my_hash_table = {:a => 1, :b => 2, :c => 3}
  11. my_hash_table[:b]
  12. # => 2
  13. ######################
  14. # Yielding to blocks #
  15. ######################
  16. # + we can add a method to a class at runtime
  17. # + self
  18. # + yield yields its arguments to a block
  19. class Integer
  20. def custom_each
  21. i = 0
  22. while i < self
  23. i = i + 1
  24. yield i
  25. end
  26. end
  27. end
  28. 3.custom_each do |num| puts "num: #{num}" end
  29. # Yay, I was right!
  30. # Can yield yield more than 1 thing?
  31. class Integer
  32. def custom_each_and_square
  33. i = 0
  34. while i < self
  35. i = i + 1
  36. yield i, i*i
  37. end
  38. end
  39. end
  40. 3.custom_each_and_square do
  41. |num, squared|
  42. puts "num: #{num}, squared: #{squared}"
  43. end
  44. # Boom!
  45. # num: 1, squared: 1
  46. # num: 2, squared: 4
  47. # num: 3, squared: 9
  48. #######################
  49. # Blocks as arguments #
  50. #######################
  51. # From playing around:
  52. # (Proc.new do |a| puts "arg: #{a}" end).call(1)
  53. # (lambda do |a| puts "arg: #{a}" end).call(1)
  54. # And it seems blocks work similarly with a call to `call`:
  55. def call_block (&block)
  56. block.call('test')
  57. end
  58. # What does the `&` mean? "dereference pointer"?
  59. val_in_environment = 3
  60. call_block do
  61. |sth, val_in_environment|
  62. puts "Hello, block! #{sth} #{val_in_environment}"
  63. end
  64. # Basically blocks are lambda expressions or closures, but not really,
  65. # because they cannot make use of bindings in the environment. Or is
  66. # there a way to access those?
  67. def call_proc proc
  68. proc.call('test')
  69. end
  70. val_in_environment = 3
  71. my_proc = lambda do
  72. |sth|
  73. puts "Hello, proc! #{sth} #{val_in_environment}"
  74. end
  75. call_proc(my_proc)
  76. # Works.
  77. def call_lambda lamb
  78. lamb.call('test')
  79. end
  80. val_in_environment = 3
  81. my_lambda = lambda do
  82. |sth|
  83. puts "Hello, lambda! #{sth} #{val_in_environment}"
  84. end
  85. call_lambda(my_lambda)
  86. # Works.
  87. ##############
  88. # Superclass #
  89. ##############
  90. 4.class
  91. 4.class.superclass
  92. 4.class.superclass.superclass
  93. 4.class.superclass.superclass.superclass
  94. 4.class.superclass.superclass.superclass.superclass
  95. nil
  96. .class.superclass.superclass.superclass
  97. .class.superclass.superclass.superclass
  98. .class.superclass.superclass.superclass
  99. .class.superclass.superclass.superclass
  100. .class.superclass.superclass.superclass
  101. .class.superclass.superclass.superclass
  102. .class.superclass.superclass.superclass
  103. # ... for as long as you want.
  104. #######################
  105. # Define a tree class #
  106. #######################
  107. class Tree
  108. # Apparently we can declare accessible fields like this,
  109. # some meta programming stuff going on here, building
  110. # accessors (and setter?) out of symbols. A shortcut.
  111. attr_accessor :children, :node_name
  112. # constructor - is the name a convention or language built-in?
  113. # default arguments
  114. def initialize(name, children=[])
  115. # apparently accessing field inside the class can be
  116. # done with `@`. Similar to Python's `self.children`,
  117. # just shorter.
  118. @children = children
  119. @node_name = name
  120. end
  121. def visit_all(&block)
  122. # Visit this one child.
  123. visit(&block)
  124. # Visit its children.
  125. children.each do
  126. |child|
  127. child.visit_all(&block)
  128. end
  129. end
  130. def visit(&block)
  131. block.call self
  132. end
  133. end
  134. # -> This example shows how the visitor pattern can be
  135. # simply done by block given as an argument, similar to how
  136. # it can be done in functional languages, by simply giving a
  137. # function as an argument.
  138. ruby_tree = Tree.new(
  139. "Ruby",
  140. [Tree.new("Reia"),
  141. Tree.new("MacRuby")])
  142. puts "Visiting a node"
  143. ruby_tree.visit do
  144. |node|
  145. puts "node name: #{node.node_name}"
  146. end
  147. puts ""
  148. puts "Visiting all nodes in the tree"
  149. ruby_tree.visit_all do
  150. |node|
  151. puts "node name: #{node.node_name}"
  152. end
  153. puts ""
  154. #############################
  155. # READING AND WRITING FILES #
  156. #############################
  157. # Ruby uses modules to define functions, which then can be
  158. # added to any class. -- What happens when 2 modules have
  159. # the same names for any of their functions?
  160. module ToFile
  161. def filename
  162. "object_#{self.object_id}.txt"
  163. end
  164. def to_file
  165. File.open(filename, 'w') do
  166. |file|
  167. # to_s is defined later, inside the class. The module
  168. # can already reference it, as the reference will be
  169. # resolved at runtime. There is no check for the
  170. # function to exist at compile time. Suppose the
  171. # method `to_s` was never implemented in the class. In
  172. # that case, if the function `to_file` never gets
  173. # called, then there would be no error. The "contract"
  174. # is implicit. By convention you will have to
  175. # implement the referenced methods.
  176. file.write(to_s)
  177. end
  178. end
  179. end
  180. # Now create a class, which will get the functions from the
  181. # ToFile module.
  182. class Person
  183. # Here we add the functions from the ToFile module.
  184. include ToFile
  185. attr_accessor :name
  186. def initialize (name)
  187. @name = name
  188. end
  189. # The definition of to_s, referenced from the module.
  190. def to_s
  191. name
  192. end
  193. end
  194. Person.new('matz').to_file
  195. # One might call the module a "mixin", as it adds the
  196. # capability of writing an object to a file, if only the
  197. # object has a set of methods, which the module makes use
  198. # of.
  199. # Lets try modules with functions of equal name.
  200. module A
  201. def foo name
  202. puts "Foo #{name}!"
  203. end
  204. def foobar name
  205. puts "Foobar-foo #{name}!"
  206. end
  207. end
  208. module B
  209. def bar name
  210. puts "Foo #{name}!"
  211. end
  212. def foobar name
  213. puts "Foobar-bar #{name}!"
  214. end
  215. end
  216. class AB
  217. include A
  218. include B
  219. end
  220. AB.new.foobar("Herbert")
  221. # Foobar-bar Herbert!
  222. # => nil
  223. # Apparently the implementation of `foobar` of the last
  224. # include is used.
  225. # Includes are inherited.
  226. # It seems, that `include` behaves, as if simple the
  227. # module's text was copied into the class definition. But
  228. # maybe there is more logic to it than that.
  229. ########################################
  230. # Standard Library Mixins / Interfaces #
  231. ########################################
  232. puts "#{'a' <=> 'b'}"
  233. lst = [2,547,23,6]