123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- ###########
- # Symbols #
- ###########
- # prefix by a colon
- :a
- :b
- :utf
- :anything
- # Hashtables
- my_hash_table = {:a => 1, :b => 2, :c => 3}
- my_hash_table[:b]
- # => 2
- ######################
- # Yielding to blocks #
- ######################
- # + we can add a method to a class at runtime
- # + self
- # + yield yields its arguments to a block
- class Integer
- def custom_each
- i = 0
- while i < self
- i = i + 1
- yield i
- end
- end
- end
- 3.custom_each do |num| puts "num: #{num}" end
- # Yay, I was right!
- # Can yield yield more than 1 thing?
- class Integer
- def custom_each_and_square
- i = 0
- while i < self
- i = i + 1
- yield i, i*i
- end
- end
- end
- 3.custom_each_and_square do
- |num, squared|
- puts "num: #{num}, squared: #{squared}"
- end
- # Boom!
- # num: 1, squared: 1
- # num: 2, squared: 4
- # num: 3, squared: 9
- #######################
- # Blocks as arguments #
- #######################
- # From playing around:
- # (Proc.new do |a| puts "arg: #{a}" end).call(1)
- # (lambda do |a| puts "arg: #{a}" end).call(1)
- # And it seems blocks work similarly with a call to `call`:
- def call_block (&block)
- block.call('test')
- end
- # What does the `&` mean? "dereference pointer"?
- val_in_environment = 3
- call_block do
- |sth, val_in_environment|
- puts "Hello, block! #{sth} #{val_in_environment}"
- end
- # Basically blocks are lambda expressions or closures, but not really,
- # because they cannot make use of bindings in the environment. Or is
- # there a way to access those?
- def call_proc proc
- proc.call('test')
- end
- val_in_environment = 3
- my_proc = lambda do
- |sth|
- puts "Hello, proc! #{sth} #{val_in_environment}"
- end
- call_proc(my_proc)
- # Works.
- def call_lambda lamb
- lamb.call('test')
- end
- val_in_environment = 3
- my_lambda = lambda do
- |sth|
- puts "Hello, lambda! #{sth} #{val_in_environment}"
- end
- call_lambda(my_lambda)
- # Works.
- ##############
- # Superclass #
- ##############
- 4.class
- 4.class.superclass
- 4.class.superclass.superclass
- 4.class.superclass.superclass.superclass
- 4.class.superclass.superclass.superclass.superclass
- nil
- .class.superclass.superclass.superclass
- .class.superclass.superclass.superclass
- .class.superclass.superclass.superclass
- .class.superclass.superclass.superclass
- .class.superclass.superclass.superclass
- .class.superclass.superclass.superclass
- .class.superclass.superclass.superclass
- # ... for as long as you want.
- #######################
- # Define a tree class #
- #######################
- class Tree
- # Apparently we can declare accessible fields like this,
- # some meta programming stuff going on here, building
- # accessors (and setter?) out of symbols. A shortcut.
- attr_accessor :children, :node_name
- # constructor - is the name a convention or language built-in?
- # default arguments
- def initialize(name, children=[])
- # apparently accessing field inside the class can be
- # done with `@`. Similar to Python's `self.children`,
- # just shorter.
- @children = children
- @node_name = name
- end
- def visit_all(&block)
- # Visit this one child.
- visit(&block)
- # Visit its children.
- children.each do
- |child|
- child.visit_all(&block)
- end
- end
- def visit(&block)
- block.call self
- end
- end
- # -> This example shows how the visitor pattern can be
- # simply done by block given as an argument, similar to how
- # it can be done in functional languages, by simply giving a
- # function as an argument.
- ruby_tree = Tree.new(
- "Ruby",
- [Tree.new("Reia"),
- Tree.new("MacRuby")])
- puts "Visiting a node"
- ruby_tree.visit do
- |node|
- puts "node name: #{node.node_name}"
- end
- puts ""
- puts "Visiting all nodes in the tree"
- ruby_tree.visit_all do
- |node|
- puts "node name: #{node.node_name}"
- end
- puts ""
- #############################
- # READING AND WRITING FILES #
- #############################
- # Ruby uses modules to define functions, which then can be
- # added to any class. -- What happens when 2 modules have
- # the same names for any of their functions?
- module ToFile
- def filename
- "object_#{self.object_id}.txt"
- end
- def to_file
- File.open(filename, 'w') do
- |file|
- # to_s is defined later, inside the class. The module
- # can already reference it, as the reference will be
- # resolved at runtime. There is no check for the
- # function to exist at compile time. Suppose the
- # method `to_s` was never implemented in the class. In
- # that case, if the function `to_file` never gets
- # called, then there would be no error. The "contract"
- # is implicit. By convention you will have to
- # implement the referenced methods.
- file.write(to_s)
- end
- end
- end
- # Now create a class, which will get the functions from the
- # ToFile module.
- class Person
- # Here we add the functions from the ToFile module.
- include ToFile
- attr_accessor :name
- def initialize (name)
- @name = name
- end
- # The definition of to_s, referenced from the module.
- def to_s
- name
- end
- end
- Person.new('matz').to_file
- # One might call the module a "mixin", as it adds the
- # capability of writing an object to a file, if only the
- # object has a set of methods, which the module makes use
- # of.
- # Lets try modules with functions of equal name.
- module A
- def foo name
- puts "Foo #{name}!"
- end
- def foobar name
- puts "Foobar-foo #{name}!"
- end
- end
- module B
- def bar name
- puts "Foo #{name}!"
- end
- def foobar name
- puts "Foobar-bar #{name}!"
- end
- end
- class AB
- include A
- include B
- end
- AB.new.foobar("Herbert")
- # Foobar-bar Herbert!
- # => nil
- # Apparently the implementation of `foobar` of the last
- # include is used.
- # Includes are inherited.
- # It seems, that `include` behaves, as if simple the
- # module's text was copied into the class definition. But
- # maybe there is more logic to it than that.
- ########################################
- # Standard Library Mixins / Interfaces #
- ########################################
- puts "#{'a' <=> 'b'}"
- lst = [2,547,23,6]
|