# Definicja metody o nazwie factorial z jednym parametrem o nazwie n.
def factorial(n)
  if n < 1                # Sprawdzenie, czy warto argumentu jest poprawna.
    raise "argument musi by > 0"
  elsif n == 1            # Jeli argument ma warto 1,
    1                     # wartoci wywoania metody jest 1.
  else                    # W przeciwnym przypadku silnia n wynosi n razy
    n * factorial(n-1)    # factorial n-1.
  end
end

def factorial(n)
  raise "zy argument" if n < 1
  return 1 if n == 1
  n * factorial(n-1)
end

# Konwersja punktu kartezjaskiego (x,y) na wsprzdne biegunowe.
def polar(x,y)
  return Math.hypot(y,x), Math.atan2(y,x)
end

# Konwersja wsprzdnych biegunowych na kartezjaskie.
def cartesian(magnitude, angle)
  [magnitude*Math.cos(angle), magnitude*Math.sin(angle)]
end

distance, theta = polar(x,y)
x, y = cartesian(distance,theta)

first = text.index(pattern)

o = "message"    # acuch jest obiektem.
def o.printme    # Definicja metody singletonowej dla tego obiektu.
  puts self
end
o.printme        # Wywoanie metody singletonowej.


def sum(x,y); x+y; end      # Definicja metody.
puts sum(1,2)               # Uycie metody.
undef sum                   # Oddefiniowanie metody.

def +(other)               # Definicja dwuargumentowego operatora dodawania: x+y rwna si x.+(y).
  self.concatenate(other)
end

alias aka also_known_as   # alias nowa_nazwa stara_nazwa

def hello                       # Fajna prosta metoda.
  puts "Witaj wiecie"          # Zamy, e chcesz j nieco wzbogaci...
end
alias original_hello hello      # Nadajesz metodzie zapasow nazw.
def hello                       # Definiujesz now metod ze star nazw.
  puts "Prosz o uwag"         # Metoda co robi.
  original_hello                # Nastpnie wywouje oryginaln metod.
  puts "To byy wiczenia"      # Metoda robi co jeszcze.
end

puts "Witaj wiecie"
puts("Witaj wiecie")

greeting = "Hello"
size = greeting.length

size = greeting.length()

x = 3              # x jest liczb.
x.between? 1,5     # To samo co x.between?(1,5).

def sum x, y
  x+y
end

puts sum 2,2

puts sum(2,2)

puts(sum 2,2)   # To znaczy puts(sum(2,2)) czy puts(sum(2), 2)?

puts factorial x   # To moe oznacza tylko puts(factorial(x)).

puts 4, sum 2,2   # Bd: ten drugi przecinek naley do pierwszej czy drugiej metody?
[sum 2,2]         # Bd: dwa elementy tablicowe czy jeden?

square(2+2)*2    # square(4)*2 = 16*2 = 32
square (2+2)*2   # square(4*2) = square(8) = 64

square((2+2)*2)

puts(sum 2,2)   # To znaczy puts(sum(2,2)) czy puts(sum(2), 2)?

puts (sum 2,2)

def prefix(s, len=1)
  s[0,len]
end

prefix("Ruby", 3)    # => "Rub"
prefix("Ruby")       # => "R"

# Zwraca ostatni znak s lub podacuch zaczynajcy si w miejscu index i trwajcy do koca.
def suffix(s, index=s.size-1)
  s[index, s.size-index]
end

# Dodanie wartoci x do tablicy a, zwrcenie a.
# Jeli nie zostaa podana adna tablica, zostaje utworzona pusta tablica.
def append(x, a=[])
  a << x
end

# Zwraca najwikszy z przekazanych argumentw.
def max(first, *rest)
  # Zaoenie, e pierwszy wymagany argument jest najwikszy.
  max = first
  # Iteracja przez wszystkie opcjonalne argumenty w celu sprawdzenia, czy ktry jest wikszy.
  rest.each {|x| max = x if x > max }
  # Zwrcenie najwikszego znalezionego argumentu.
  max
end

max(1)       # first=1, rest=[]
max(1,2)     # first=1, rest=[2]
max(1,2,3)   # first=1, rest=[2,3]

data = [3, 2, 1]
m = max(*data)   # first = 3, rest=[2,1] => 3

m = max(data)   # first = [3,2,1], rest=[] => [3,2,1]

# Konwersja punktu (x,y) na wsprzdne biegunowe i z powrotem na kartezjaskie.
x,y = cartesian(*polar(x, y))

max(*"witaj".each_char)  # => 'w'

# Niniejsza metoda zwraca tablic n liczb. Dla kadego indeksu i, 0 <= i < n,
# warto elementu a[i] wynosi m*i+c. Argumenty n, m i c s przekazywane jako
# klucze w tablicy asocjacyjnej, dziki czemu nie trzeba pamita ich kolejnoci.
def sequence(args)
  # Pobranie argumentw z tablicy asocjacyjnej.
  # Zwr uwag na uycie operatora || do okrelenia wartoci domylnych uywanych,
  # jeli tablica asocjacyjna nie zawiera szukanego klucza.
  n = args[:n] || 0
  m = args[:m] || 1
  c = args[:c] || 0
  a = []                      # Pusta tablica.
  n.times {|i| a << m*i+c }   # Obliczenie wartoci kadego elementu tablicy.
  a                           # Zwrcenie tablicy.
end

sequence({:n=>3, :m=>5})      # => [0, 5, 10]

sequence(:m=>3, :n=>5)        # => [0, 3, 6, 9, 12]

# Skadnia Ruby 1.9
sequence c:1, m:3, n:5        # => [1, 4, 7, 10, 13]

sequence {:m=>3, :n=>5}       # Bd skadni!

# Generuje szereg n liczb m*i + c oraz przekazuje je do bloku.
def sequence2(n, m, c)
  i = 0
  while(i < n)         # Powtrzenie n razy.
    yield i*m + c      # Przekazanie kolejnego elementu szeregu do bloku.
    i += 1
  end
end
# Przykadowy sposb uycia powyszej metody.
sequence2(5, 2, 2) {|x| puts x }  # Drukuje liczby 2, 4, 6, 8, 10.

def sequence3(n, m, c, &b) # Jawny argument pozwalajcy zamieni blok na obiekt klasy Proc.
  i = 0
  while(i < n)
    b.call(i*m + c)        # Wywoanie obiektu klasy Proc za pomoc jego metody call.
    i += 1
  end
end
# Blok jest nadal przekazywany poza nawiasami.
sequence3(5, 2, 2) {|x| puts x }

# Niniejsza wersja przyjmuje jawnie utworzony obiekt klasy Proc, nie blok.
def sequence4(n, m, c, b)  # Argument b nie ma prefiksu &.
  i = 0
  while(i < n)
    b.call(i*m + c)        # Jawne wywoanie obiektu klasy Proc.
    i += 1
  end
end
p = Proc.new {|x| puts x }  # Jawne utworzenie obiektu klasy Proc.
sequence4(5, 2, 2, p)       # Przekazanie go jako zwykego argumentu.

def sequence5(args, &b) # Przekazanie argumentw jako tablicy haszowej i dodanie bloku.
  n, m, c = args[:n], args[:m], args[:c]
  i = 0
  while(i < n)
    b.call(i*m + c)
    i += 1
  end
end
# Przyjmuje jeden lub wicej argumentw, po ktrych nastpuje blok.
def max(first, *rest, &block)
  max = first
  rest.each {|x| max = x if x > max }
  block.call(max)
  max
end

a, b = [1,2,3], [4,5]                     # Jakie dane pocztkowe.
sum = a.inject(0) {|total,x| total+x }    # => 6. Suma elementw tablicy a.
sum = b.inject(sum) {|total,x| total+x }  # => 15. Dodanie elementw z tablicy b.

a, b = [1,2,3], [4,5]                     # Jakie dane pocztkowe.
summation = Proc.new {|total,x| total+x } # Obiekt klasy Proc.
sum = a.inject(0, &summation)             # => 6
sum = b.inject(sum, &summation)           # => 15

words = ['oraz', 'but', 'kot']    # Tablica sw.
uppercase = words.map &:upcase    # Konwersja na wielkie litery za pomoc metody String.upcase.
upper = words.map {|w| w.upcase } # Ten sam kod zapisany z uyciem bloku.

# Niniejsza metoda tworzy obiekt proc z bloku.
def makeproc(&p)  # Konwersja bloku na obiekt klasy Proc i zapisanie go w zmiennej p.
  p               # Zwrcenie obiektu klasy Proc.
end

adder = makeproc {|x,y| x+y }

sum = adder.call(2,2)  # => 4

p = Proc.new {|x,y| x+y }

def invoke(&b)     def invoke
  b.call             Proc.new.call
end                end

is_positive = lambda {|x| x > 0 }

succ = lambda {|x| x+1}

succ = ->(x){ x+1 }

succ.call(2)    # => 3

# Niniejszy obiekt lambda pobiera dwa argumenty i deklaruje trzy zmienne lokalne.
f = ->(x,y; i,j,k) { ... }

zoom = ->(x,y,factor=2) { [x*factor, y*factor] }

succ = ->x { x+1 }
f = -> x,y; i,j,k { ... }
zoom = ->x,y,factor=2 { [x*factor, y*factor] }

->{}

def compose(f,g)            # Kombinacja dwch lambd.
  ->(x) { f.call(g.call(x)) }
end
succOfSquare = compose(->x{x+1}, ->x{x*x})
succOfSquare.call(4)        # => 17: Wynik dziaania (4*4)+1

data.sort {|a,b| b-a }   # Blok.
data.sort &->(a,b){ b-a } # Litera lambdy.

f = Proc.new {|x,y| 1.0/(1.0/x + 1.0/y) }
z = f.call(x,y)

z = f[x,y]

z = f.(x,y)

      product = ->(x,y){ x*y }  # Definicja lambdy,
      triple = product.curry[3] # Przeksztacenie, a potem zastosowanie pierwszego argumentu.
      [triple[10],triple[20]]   # => [30,60]
      lambda {|w,x,y,z| w+x+y+z}.curry[1][2,3][4] # => 10

lambda{||}.arity        # => 0. Nie wymaga adnych argumentw.
lambda{|x| x}.arity     # => 1. Wymaga jednego argumentu.
lambda{|x,y| x+y}.arity # => 2. Wymaga dwch argumentw.

lambda {|*args|}.arity        # => -1.  ~-1 = -(-1)-1 = 0 wymaganych argumentw.
lambda {|first, *rest|}.arity # => -2.  ~-2 = -(-2)-1 = 1 wymagany argument.

puts lambda {}.arity  # 1 w 1.8; 0 w Ruby 1.9

lambda {|x| x*x } == lambda {|x| x*x }  # => false

p = lambda {|x| x*x }
q = p.dup
p == q                      # => true: obiekty s rwne
p.object_id == q.object_id  # => false: nie s tym samym obiektem

def test
  puts "wejscie do metody"
  1.times { puts "wejcie do bloku"; return }  # Zmusza do wyjcia z metody test.
  puts "wyjcie z metody"  # Ten wiersz nie jest nigdy wykonywany.
end
test

def test
  puts "wejcie do metody"
  p = Proc.new { puts "wejcie do obiektu proc"; return }
  p.call                   # Wywoanie obiektu proc zmusza metod do powrotu.
  puts "wyjcie z metody"  # Ten wiersz nie jest nigdy wykonywany.
end
test

def procBuilder(message)            # Utworzenie i zwrcenie obiektu proc.
  Proc.new { puts message; return } # Instrukcja return powoduje wyjcie z metody procBuilder.
  # Ale metoda procBuilder zwrcia ju warto tutaj!
end
def test
  puts "wejcie do metody"
  p = procBuilder("wejcie do obiektu proc")
  p.call                   # Drukuje "wejcie do obiektu proc" i zgasza wyjtek LocalJumpError!
  puts "wyjcie z metody"  # Ten wiersz nie jest nigdy wykonywany.
end
test

def test
  puts "wejcie do metody"
  p = lambda { puts "wejcie do lambdy"; return }
  p.call                   # Wywoanie lambdy nie zmusza metody do powrotu.
  puts "wyjcie z metody"  # Ten wiersz *jest* tym razem wykonywany.
end
test

def lambdaBuilder(message)        # Utworzenie i zwrcenie lambdy.
  lambda { puts message; return } # Instrukcja return zmusza lambd do zwrotu wartoci.
end
def test
  puts "wejcie do metody"
  l = lambdaBuilder("wejcie do lambdy")
  l.call                   # Drukuje "wejcie do lambdy".
  puts "wyjcie z metody"  # Ten wiersz jest wykonywany.
end
test

def test
  puts "wejcie do metody test"
  proc = Proc.new { puts "wejcie do obiektu proc"; break }
  proc.call                    # LocalJumpError: iterator ju zwrci warto.
  puts "wyjcie z metody test"
end
test

def iterator(&proc)
  puts "wejcie do iteratora"
  proc.call  # Wywoanie obiektu proc.
  puts "wyjcie z iteratora"   # Kod ten nie jest wykonywany, jeli obiekt proc wywouje instrukcj break.
end
def test
  iterator { puts "wejcie do obiektu proc"; break }
end
test

def test
  puts "wejcie do metody test"
  lambda = lambda { puts "wejcie do lambdy"; break; puts "wyjcie z lambdy" }
  lambda.call
  puts "wyjcie z metody test"
end
test

p = Proc.new {|x,y| print x,y }
p.call(1)       # x,y=1:     w miejscu brakujcej r-wartoci zostanie uyta warto nil: Drukuje 1nil.
p.call(1,2)     # x,y=1,2:   2 l-wartoci, 2 r-wartoci:         drukuje 12.
p.call(1,2,3)   # x,y=1,2,3: dodatkowa r-warto zostaje odrzucona: drukuje 12.
p.call([1,2])   # x,y=[1,2]: tablica zostaje automatycznie rozpakowana: drukuje 12.

l = lambda {|x,y| print x,y }
l.call(1,2)     # To dziaa.
l.call(1)       # Za liczba argumentw.
l.call(1,2,3)   # Za liczba argumentw.
l.call([1,2])   # Za liczba argumentw.
l.call(*[1,2])  # Dziaa: operator splat rozpakowuje tablic.

# Mnoy kady element tablicy data przez n.
def multiply(data, n)
  data.collect {|x| x*n }
end
puts multiply([1,2,3], 2)   # Drukuje 2,4,6.

# Zwraca lambd, ktra zachowuje argument n.
def multiplier(n)
  lambda {|data| data.collect{|x| x*n } }
end
doubler = multiplier(2)     # Uycie lambdy, ktra podwaja kad warto.
puts doubler.call([1,2,3])  # Drukuje 2,4,6.

# Zwraca dwie lambdy majce dostp do tej samej zmiennej lokalnej.
def accessor_pair(initialValue=nil)
  value = initialValue  # Zmienna lokalna wspdzielona przez utworzone lambdy.
  getter = lambda { value }          # Zwrot wartoci zmiennej lokalnej.
  setter = lambda {|x| value = x }   # Zmiana wartoci zmiennej lokalnej.
  return getter, setter              # Zwrot dwch lambd do algorytmu wywoujcego.
end
getX, setX = accessor_pair(0) # Utworzenie lambd dostpowych dla pocztkowej wartoci 0.
puts getX[]        # Drukuje 0. Zamiast metody call uyto nawiasw kwadratowych.
setX[10]           # Zmiana wartoci przez jedno z domkni.
puts getX[]        # Drukuje 10. Zmiana jest widoczna w drugim domkniciu.

# Zwraca tablic lambd mnoonych przez argumenty.
def multipliers(*args)
  x = nil
  args.map {|x| lambda {|y| x*y }}
end
double,triple = multipliers(2,3)
puts double.call(2)    # Drukuje 6 w Ruby 1.8.

# Zwraca lambd, ktra zachowuje argument n.
def multiplier(n)
  lambda {|data| data.collect{|x| x*n } }
end
doubler = multiplier(2)     # Uycie lambdy, ktra podwaja kad warto.
puts doubler.call([1,2,3])  # Drukuje 2,4,6.

eval("n=3", doubler.binding) # Lub doubler.binding.eval("n=3") w Ruby 1.9.
puts doubler.call([1,2,3])   # Teraz drukuje 3,6,9!

eval("n=3", doubler)

m = 0.method(:succ)  # Obiekt klasy Method reprezentujcy metod succ obiektu 0 klasy Fixnum.

puts m.call    # To samo co puts 0.succ. lub puts m[].

def square(x); x*x; end
puts (1..10).map(&method(:square))

unbound_plus = Fixnum.instance_method("+")

plus_2 = unbound_plus.bind(2)   # Zwizanie metody z obiektem 2.

sum = plus_2.call(2)    # => 4

plus_3 = plus_2.unbind.bind(3)

# Oblicza redni i standardowe odchylenie w tablicy liczb.
mean = a.inject {|x,y| x+y } / a.size
sumOfSquares = a.map{|x| (x-mean)**2 }.inject{|x,y| x+y }
standardDeviation = Math.sqrt(sumOfSquares/(a.size-1))

# Niniejszy modu zawiera definicje metod i operatorw przeznaczonych do programowania funkcjonalnego.
module Functional
  # Funkcja ta zostanie zastosowana do kadego elementu wyznaczonego obiektu umoliwiajcego iteracj
  # i zwrci tablic wynikw. Jest to odwrotno metody Enumerable.map.
  # Znak | jest uywany jako alias operatora. Naley go czyta zastosowany na rzecz.
  #
  # Przykad:
  #   a = [[1,2],[3,4]]
  #   sum = lambda {|x,y| x+y}
  #   sums = sum|a   # => [3,7]
  def apply(enum)
    enum.map &self
  end
  alias | apply
  # Funkcja ta redukuje obiekt umoliwiajcy iteracj do pojedynczej wartoci.
  # Odwrotno metody Enumerable.inject.
  # Aliasem operatora jest <=.
  # Wskazwka: <= wyglda jak iga do robienia zastrzykw.
  # Przykad:
  #   data = [1,2,3,4]
  #   sum = lambda {|x,y| x+y}
  #   total = sum<=data   # => 10
  def reduce(enum)
    enum.inject &self
  end
  alias <= reduce
end
# Dodanie tych metod programowania funkcjonalnego do klas Proc i Method.
class Proc; include Functional; end
class Method; include Functional; end

sum = lambda {|x,y| x+y }        # Funkcja dodajca dwie liczby.
mean = (sum<=a)/a.size           # Albo sum.reduce(a), albo a.inject(&sum).
deviation = lambda {|x| x-mean } # Funkcja obliczajca rnic ze redniej.
square = lambda {|x| x*x }       # Funkcja podnoszca liczb do kwadratu.
standardDeviation = Math.sqrt((sum<=square|(deviation|a))/(a.size-1))

module Functional
  # Zwraca now lambd obliczajc self[f[args]].
  # Uycie operatora * jako aliasu dla metody compose.
  # Przykady z uyciem aliasu * dla tej metody.
  #
  # f = lambda {|x| x*x }
  # g = lambda {|x| x+1 }
  # (f*g)[2]   # => 9
  # (g*f)[2]   # => 5
  #
  # def polar(x,y)
  #   [Math.hypot(y,x), Math.atan2(y,x)]
  # end
  # def cartesian(magnitude, angle)
  #   [magnitude*Math.cos(angle), magnitude*Math.sin(angle)]
  # end
  # p,c = method :polar, method :cartesian
  # (c*p)[3,4]  # => [3,4]
  #
  def compose(f)
    if self.respond_to?(:arity) && self.arity == 1
      lambda {|*args| self[f[*args]] }
    else
      lambda {|*args| self[*f[*args]] }
    end
  end
  # * jest naturalnym operatorem czenia funkcji.
  alias * compose
end

standardDeviation = Math.sqrt((sum<=square*deviation|a)/(a.size-1))

product = lambda {|x, y| x*y }       # Funkcja z dwoma argumentami.
double = lambda {|x| product(2,x) }  # Aplikacja jednego argumentu.

module Functional
  #
  # Zwraca lambd odpowiadajc tej z zastosowanym jednym lub wiksz liczb
  # pocztkowych argumentw. Kiedy podany jest tylko jeden argument,
  # prostszy w uyciu moe by alias >>.
  # Przykad:
  #   product = lambda {|x,y| x*y}
  #   doubler = lambda >> 2
  #
  def apply_head(*first)
    lambda {|*rest| self[*first.concat(rest)]}
  end
  #
  # Zwraca lambd odpowiadajc tej z zastosowanym jednym lub wiksz liczb kocowych
  # argumentw. Kiedy podany jest tylko jeden argument,
  # prostszy moe by alias <<.
  # Przykad:
  #  difference = lambda {|x,y| x-y }
  #  decrement = difference << 1
  #
  def apply_tail(*last)
    lambda {|*rest| self[*rest.concat(last)]}
  end
  # Alternatywne operatory dla tych metod. Nawiasy ostre
  # wskazuj, po ktrej stronie argument jest wsuwany.
  alias >> apply_head    # g = f >> 2 -- ustawienie pierwszego argumentu na 2.
  alias << apply_tail    # g = f << 2 -- ustawienie ostatniego argumentu na 2.
end

difference = lambda {|x,y| x-y }  # Oblicza rnic dwch liczb.
deviation = difference<<mean      # Aplikacja drugiego argumentu.

module Functional
  #
  # Zwraca now lambd, ktra zapamituje wyniki tej funkcji i
  # wywouje j tylko wwczas, gdy zostan podane nowe argumenty.
  #
  def memoize
    cache = {}  # Pusta pami podrczna. Lambda obejmuje j w swoim domkniciu.
    lambda {|*args|
      # Zauwa, e klucz tablicy asocjacyjnej jest ca tablic argumentw!
      unless cache.has_key?(args)  # Jeli nie ma jeszcze zapisanych wynikw dla tych argumentw,
        cache[args] = self[*args]  # wykonuje obliczenia i zapisuje wynik.
      end
      cache[args]                  # Zwraca wynik z pamici podrcznej.
    }
  end
  # Jednoargumentowy operator + (prawdopodobnie niepotrzebny) dla spamitywania.
  # Wskazwka: operator + oznacza "ulepszony".
  alias +@ memoize        # cached_f = +f
end

# Spamitywana rekursywna funkcja factorial.
factorial = lambda {|x| return 1 if x==0; x*factorial[x-1]; }.memoize
# Uycie operatora +.
factorial = +lambda {|x| return 1 if x==0; x*factorial[x-1]; }

factorial = lambda {|x| return 1 if x==0; x*factorial[x-1]; }
cached_factorial = +factorial # Wywoania rekursywne nie s zapisywane w pamici podrcznej!

# Zwikszenie tablicy liczb cakowitych za pomoc metody Fixnum.succ.
[1,2,3].map(&:succ)  # => [2,3,4]

[1,2,3].map {|n| n.succ }

class Symbol
  def to_proc
    lambda {|receiver, *args| receiver.send(self, *args)}
  end
end

class Symbol
  def to_proc
    lambda {|receiver, *args| receiver.method(self)[*args]}
  end
end

class Module
  # Dostp do metod obiektowych przy uyciu notacji tablicowej. Zwraca obiekt klasy UnboundMethod.
  alias [] instance_method
end

String[:reverse].bind("hello").call   # => "olleh"

class UnboundMethod
  # Zezwolenie na uywanie [] jako alternatywnego sposobu wizania.
  alias [] bind
end

String[:reverse]["hello"][]   # => "olleh"

class Module
  # Definicja metody obiektowej o nazwie sym i ciele f.
  # Przykad: String[:backwards] = lambda { reverse }
  def []=(sym, f)
    self.instance_eval { define_method(sym, f) }
  end
end

Enumerable[:average] = lambda do
  sum, n = 0.0, 0
  self.each {|x| sum += x; n += 1 }
  if n == 0
    nil
  else
    sum/n
  end
end

#
# Dodanie operatorw [] i []= do klasy Symbol, ktre daj dostp i pozwalaj ustawia
# metody singletonowe obiektw. Znak : naley czyta jako metoda, a [] odpowiada na pytanie czego.
# Zatem :m[o] naley czyta "metoda m obiektu o".
#
class Symbol
  # Zwraca obiekt klasy Method obiektu obj wyznaczonego przez ten symbol. Moe to by metoda singletonowa
  # obiektu obj (jak metoda klasowa) lub metoda obiektowa zdefiniowana
  # przez obj.class lub odziedziczona po nadklasie.
  # Przykady:
  #   creator = :new[Object]  # Metoda klasowa Object.new.
  #   doubler = :*[2]         # Metoda * obiektu klasy Fixnum 2.
  #
  def [](obj)
    obj.method(self)
  end

  # Definicja metody singletonowej dla obiektu o przy uyciu obiektu klasy Proc lub Method f jako jej ciaa.
  # Ten symbol suy jako nazwa metody.
  # Przykady:
  #
  #  :singleton[o] = lambda { puts "to jest metoda singletonowa obiektu o" }
  #  :class_method[String] = lambda { puts "to jest metoda klasowa" }
  #
  # Zauwa, e nie mona utworzy w ten sposb metody obiektowej. Zobacz Module.[]=
  #
  def []=(o,f)
    # W poniszym bloku nie mona uy obiektu self, poniewa jest wyznaczany w
    # kontekcie innego obiektu. W zwizku z tym self musi zosta przypisany do zmiennej.
    sym = self
    # To jest obiekt, dla ktrego definiujesz metody singletonowe.
    eigenclass = (class << o; self end)
    # Metoda define_method jest prywatna. Aby j uruchomi, konieczne jest uycie metody instance_eval.
    eigenclass.instance_eval { define_method(sym, f) }
  end
end

dashes = :*['-']       # Metoda * obiektu '-'
puts dashes[10]        # Drukuje "----------".
y = (:+[1]*:*[2])[x]   # Inny sposb zapisu y = 2*x + 1.
