Info2/2008tavasz/kuka

A MathWikiből
(Változatok közti eltérés)
 
(egy szerkesztő 40 közbeeső változata nincs mutatva)
1. sor: 1. sor:
== 3. előadás (2008-02-29) ==
+
A tárgy főoldala: [[info2/2008tavasz]]
 +
A tárgy oktatásához felhasznált, http://wiki.math.bme.hu/ -n belüli wikioldalak [http://en.wikipedia.org/wiki/GNU_Free_Documentation_License GNU FDL] licenc vagy (választás szerint) [http://hu.wikipedia.org/wiki/Creative_commons CC-BY-SA-2.0] licenc szerint szabadon használhatók és terjeszthetők.
  
=== Függvényekkel kapcsolatos alapfogalmak ===
+
== 8. előadás (2007-04-04) ==
  
* függvény
+
=== Objektum-orientált programozás ===
* visszatérési érték
+
* paraméter
+
* lokális változó
+
* érték szerinti paraméterátadás
+
* cím szerinti paraméterátadás
+
* mellékhatás
+
* rekurzió
+
  
==== Érték és cím szerinti paraméterátadás ====
 
  
~/info2/parameteratadas.c
+
Néhány új fogalom:
  
#include <stdio.h>
+
* osztály (a C-beli struktúratípusnak felel meg)
+
* objektum (a C-beli struktúrának felel meg)
void f (int x)       // x egész
+
* attribútum (a C-beli struktúramezőnek felel meg)
{
+
* metódus (a C-beli függvénynek felel meg)
  x++;
+
}
+
+
void g (int* px)     // px egy cím, ahol egy egész van tárolva
+
{                    // amelyre *px hivatkozik
+
  *px=*px+1;
+
}
+
+
int main (void)
+
{
+
  int x=3;
+
  f(x);              // f nem változtatja meg x értékét
+
  printf("x f utan = %d\n",x);
+
  g(&x);            // g megváltoztatja x értékét
+
  printf("x g utan = %d\n",x);
+
  return 0;
+
}
+
  
==== Érték szerinti átadás (két változó értékének cseréje) ====
+
Osztály ''class'' (síkidom), osztálypéldány ''class instance'' = objektum ''object'' (kör, téglalap), konstruktor ''constructor'' (új objektum létrehozása), objektum azonosító (object identifier - object ID), példány változó ''instance variable'' (mely az objektum állapotát/tulajdonságait tartalmazza, pl. középpont), példány metódus ''instance method'' (),
  
Az alábbi megoldás nem jól cserél, mert érték szerint veszi át a-t és b-t, tehát a hívás pillanatában lemásolja, és csak a másolatot cseréli, noha az eredetit kéne:
+
=== Ruby nyelv ===
  
#include <stdio.h>
+
A Ruby '''interpretált''', '''szkriptnyelv''' (interpretált, és minden rendszer szintű szolgáltatáshoz hozzá lehet férni).  
+
 
void rosszcsere(int a, int b) {
+
A Ruby osztályhierarchiája hasonló a biológiai törzsfához. Például a gerincesek (osztály) törzsén belül a madarak (osztály) osztályának egy alosztálya a pacsirta (osztály) nevű faj, melynek egy példánya a Csipcsip nevű kismadarunk (objektum), aki nem mellesleg egy pacsirta. Őt jellemezhetjük különböző tulajdonságai alapján; ezek az attribútumok. Például: él-e még? , mennyire éhes, hogy hívják a párját, stb. Tehát bizonyos tulajdonságait megadjuk, amik csak rá jellemzőek.
  int abak=a;
+
 
  a=b;
+
                      Változók                                Konstansok és
  b=abak;
+
  Lokális        Globális        Példány        Osztály        osztály nevek
  }
+
 
   
 
   
  int main(void) {
+
  valtozo        $valami        @nev            @@osztvalt      PI
  int x=5, y=6;
+
  joEjt_2        $_              @XY            @@N            String
  printf("csere elott x=%d, y=%d\n", x, y); /* 5, 6 */
+
  rosszcsere(&x, &y);
+
  printf("csere utan  x=%d, y=%d\n", x, y); /* 5, 6 -- rossz */
+
  return 0;
+
  }
+
  
==== Cím szerinti paraméterátadás ====
 
  
~/info2/csere.c -- ez már jó:
+
=== Feltételes utasítások ===
  
#include <stdio.h>
+
==== if ====
+
void csere(int *a, int *b) {
+
  int abak=*a;
+
  *a=*b;
+
  *b=abak;
+
}
+
+
int main(void) {
+
  int x=5, y=6;
+
  printf("csere elott x=%d, y=%d\n", x, y);
+
  csere(&x, &y);
+
  printf("csere utan  x=%d, y=%d\n", x, y);
+
  return 0;
+
}
+
  
Példa a hatványozás rekurzív számolására (~/szimp2/pow.c):
+
Először then-nel, majd újsorral elválasztva:
  
  /** Visszadja a az alap**kitevo hatványozás eredményét.
+
  if x==2 then x += 1 end
  * Csak akkor működik helyesen, ha kitevo>=0.
+
  * pow(0,0)==1
+
  */
+
int pow(int alap, int kitevo) {
+
  if (kitevo<=0) return 1;
+
  return alap*pow(alap, kitevo-1);
+
}
+
  
Példa moduláris hatványozás rekurzív számolására (~/szimp2/powmod.c):
+
if x==2
 +
  x += 1
 +
end
  
  /** Visszadja a az alap**kitevo hatványozás eredményének modulo modulus vett
+
  if x==2; x += 1 end
  * maradékát.
+
  * Csak akkor működik helyesen, ha alap>=0 és kitevo>=0 és modulus>=2.
+
  * powmod(0,0,modulus)==1
+
  */
+
int powmod(int alap, int kitevo, int modulus) {
+
  if (kitevo<=0) return 1;
+
  return ((alap%modulus)*pow(alap, kitevo-1, modulus))%modulus;
+
}
+
  
Példa moduláris hatványozás rekurzív számolására az előzőnnél gyorsabban (~/szimp2/powmod2.c):
+
==== else ====
  
  /** Visszadja a az alap**kitevo hatványozás eredményének modulo modulus vett
+
  if x==2 then x += 1 else x += 2 end
  * maradékát.
+
  * Csak akkor működik helyesen, ha alap>=0 és kitevo>=0 és modulus>=2.
+
  * powmod2(0,0,modulus)==1
+
  */
+
int powmod2(int alap, int kitevo, int modulus) {
+
  int ret;
+
  if (kitevo<=0) return 1;
+
  alap%=modulus;
+
  ret=powmod2(alap, kitevo/2, modulus);
+
  if (ret!=0) {
+
    ret=(ret*ret)%modulus;
+
    if (kitevo%2!=0) ret=(alap*ret)%modulus;
+
  }
+
  return ret;
+
}
+
  
A fenti powmod2 például 5 a 11-edikent modulo 10 így számolja ki:
+
if x==2
 +
  x += 1
 +
else
 +
  x += 2
 +
end
  
o=5 % 10;
+
==== elsif ====
p=o*o % 10;
+
q=5*p*p % 10;
+
r=5*q*q % 10;
+
return r;
+
  
Csemegeként (nem kerül számonkérésre) következzen
+
if x == 1
a powmod2 olyan változata, ami a lokális változók értékét nem módosítja (~/szimp2/powmod3.c):
+
  "hetfo"
 +
elsif x == 2
 +
  "kedd"
 +
elsif x == 3 then "szerda"
 +
else "hetvege"
 +
end
  
/** Visszadja a az alap**kitevo hatványozás eredményének modulo modulus vett
+
==== if kifejezésben ====
  * maradékát.
+
  * Csak akkor működik helyesen, ha alap>=0 és kitevo>=0 és modulus>=2.
+
  * powmod2(0,0,modulus)==1
+
  */
+
int powmod3(int alap, int kitevo, int modulus) {
+
  int ret;
+
  if (kitevo<=0) return 1;
+
  return powmod3b(alap%modulus, kitevo, modulus);
+
}
+
  
  /** Csak akkor működik helyesen, ha alap>=0 és alap<modulus és modulus>=2 és
+
  x = if y == 1
  * kitevo>=0.
+
      3
  */
+
    else
int powmod3b(int alap, int kitevo, int modulus) {
+
      4
  return powmod3c(alap, kitevo, modulus, powmod3b(alap, kitevo/2, modulus));
+
    end
}
+
  
  /** Csak akkor működik helyesen, ha alap>=0 és alap<modulus és modulus>=2 és
+
  x = 4
  * kitevo>=0.
+
x = 3 if y == 1
  */
+
int powmod3c(int alap, int kitevo, int modulus, int fele) {
+
  if (fele==0) return 0;
+
  return powmod3d(alap, kitvo, modulus, (fele*fele)%modulus);
+
}
+
  
/** Csak akkor működik helyesen, ha alap>=0 és alap<modulus és modulus>=2 és
+
==== unless ====
  * kitevo>=0.
+
  */
+
int powmod3d(int alap, int kitevo, int modulus, int felnegyzet) {
+
  if (kitevo%2==0) return felnegyzet;
+
  return (alap*felnegyzet)%modulus;
+
}
+
  
Megjegyzés: a powmod3 függvény Cékla nyelven is működik. A Cékla nyelvet majd a Deklaratív programozás tárgy keretében fogjuk tanulni.
+
unless x <= 4
 +
  "nagyobb, mint 4"
 +
end
  
Példa a faktoriális rekurzív számolására (~/szimp2/fakt.c):
+
unless x <= 4
 +
  "nagyobb, mint 4"
 +
else
 +
  "nem nagyobb"
 +
end
  
  int fakt(int n) {
+
  x = unless x <= 4 then 5 end
  if (n<2) return 1;
+
  return n*fakt(n-1);
+
}
+
  
Példa a faktoriális rekurzív számolására akkumulátorral és jobbrekurzióval (~/szimp2/fakt2.c; nem kell érteni, hogy miért jobb a fakt2, mint a fakt):
+
x = 5 unless x <= 4
  
int fakt2(int n) {
+
==== case ====
  return fakt2b(n, 1);
+
}
+
  
/** n!*szorzo -t adja vissza. */
+
A fenti if-es példával ekvivalens a következővel (a sorvége helyett itt is lehet then vagy pontosvessző):
int fakt2b(int n, int szorzo) {
+
  if (n<2) return szorzo;
+
  return fakt2b(n-1, szorzo*n);
+
}
+
  
Példa a Fibonacci-sorozat rekurzív számolására (~/szimp2/fib.c; ez szerepelt is a táblán előadáson):
+
case x
 +
when 1
 +
  "hetfo"
 +
when 2
 +
  "kedd"
 +
when 3; "szerda"
 +
else "hetvege"
 +
end
  
int fib(int n) {
+
Ekkor valójában a következő hajtódik végre (figyeljük meg a case-egyenlőség használatát):
  if (n<2) return n;
+
  return fib(n-1)+fib(n-2);
+
}
+
  
Miért olyan lassú fib(1000)-t kiszámolni? Azért, mert fib(1000)-hez pl. fib(1)-et és fib(2)-t rengetegszer kell kiszámolni, noha elég lenne egyszer is. Ilyenkor az iteratív (értsd: for-ciklusos) megoldás jóval gyorsabb:
+
case
 +
when 1 === x then "hetfo"
 +
when 2 === x then "kedd"
 +
when 3 === x then "szerda"
 +
else "hetvege"
 +
end
  
Példa a Fibonacci-sorozat iteratív számolására (~/szimp2/fib2.c; ez is szerepelt a táblán):
+
Az === néha megegyezik ==-vel, pl. a Fixnum osztályban, más osztályokban külön definiálva van a case számára:
  
int fib(int n) {
+
''tartomány használata:'' itt 'a === b' azt jelenti, hogy 'b' benne van-e az 'a' tartományban:
  int a, b, regib;
+
  if (n<2) return n;
+
  a=0; b=1;
+
  while (n>1) {
+
    regib=b; b+=a; a=regib;
+
    n--;
+
  }
+
  return b;
+
}
+
  
Példa a legrövidebb közös részstring hosszának rekurzív meghatározására (~/szimp2/lkrh.c). Ezt még nem kell érteni.
+
case x
 +
when 1..5
 +
  "hetkoznap"
 +
when 6..7
 +
  "hetvege"
 +
else "rossz adat"
 +
end
  
int max(int a, int b) {
+
''reguláris kifejezés használata:'' itt 'a === b' azt jelenti, hogy 'b' illeszkedik-e az 'a' mintára:
  return a>b ? a : b;
+
}
+
  
  int lkrh(char *s, char *t) {
+
  x = "ab"
  if (*s=='\0') return 0; /* ha az s üres, akkor lkrh==0 */
+
  case x
  if (*s=='\0') return 0; /* ha a t üres, akkor lkrh==0 */
+
when /[a-z]+/
  if (*s==*t) { /* az első karakter azonos */
+
   "szoveg"
    return 1+lkrh(s+1, t+1);
+
when /[0-9]+/
   } else {
+
   "szam"
    return max(lkrh(s+1, t), lkrh(s, t+1));
+
  end
   }
+
=> "szoveg"
  }
+
  
A fenti lkrh függvény túl lassú, mert rekurzív hívásakor ugyanarra az s--t párra többször is meghívódik (és tovább hívja magát rekurzívan). Jó lenne az első hívás után letárolni az eredményt.
+
egy összetettebb példa (beolvassa és kiírja a sorokat addig, míg egy 'ennyi' tartalmút nem kap, a megjegyzés sorokat kihagyja):
 +
 
 +
while line=gets.chomp do
 +
  case line
 +
  when /^\s*#/
 +
    next
 +
  when /^ennyi$/i    # i = case insensitive
 +
    break
 +
  else
 +
    puts line
 +
  end
 +
end
 +
 
 +
''osztályhoz tartozás:'' a Class osztályban 'a === b' azt jelenti, hogy a 'b' az 'a' osttály példánya-e:
 +
 
 +
case x
 +
when Numeric then "szam"
 +
when String then "sztring"
 +
else "valami mas"
 +
end
 +
 
 +
A Ruby case utasításában nincs ráfutás, így nem is kell minden ágat lezárni, mint C-beli switch utasításban break-kel vagy return-nel!!!
 +
 
 +
==== ?: ====
 +
 
 +
Ha x == 3, y legyen 4, egyébként 5:
 +
 
 +
x = y == 3 ? 4 : 5
 +
 
 +
=== Ciklus utasítások ===
 +
 
 +
==== while, until ====
 +
 
 +
>> x=0
 +
=> 0
 +
>> while x<4 do
 +
?>  x += 1
 +
>>  puts x
 +
>> end
 +
1
 +
2
 +
3
 +
4
 +
=> nil
 +
 
 +
>> x=0
 +
=> 0
 +
>> until x>=4 do
 +
?>  x += 1
 +
>>  puts x
 +
>> end
 +
1
 +
2
 +
3
 +
4
 +
=> nil
 +
 
 +
==== while, until kifejezésekben ====
 +
 
 +
>> x = 0; x += 1 while x<4; x
 +
=> 4
 +
 
 +
>> x=0; x += 1 until x==4; x
 +
=> 4
 +
 
 +
==== for-in ciklus ====
 +
 
 +
Egy tömbre és egy hash-táblára:
 +
 
 +
>> a = [1,2,3]
 +
=> [1, 2, 3]
 +
>> for i in a
 +
>>  p i
 +
>> end
 +
1
 +
2
 +
3
 +
=> [1, 2, 3]
 +
 
 +
>> h = {"hetfo"=>1, "kedd"=>2, "vasarnap"=>7}
 +
=> {"hetfo"=>1, "vasarnap"=>7, "kedd"=>2}
 +
>> for kulcs, ertek in h
 +
>>  puts "#{kulcs} a(z) #{ertek}. nap"
 +
>> end
 +
hetfo a(z) 1. nap
 +
vasarnap a(z) 7. nap
 +
kedd a(z) 2. nap
 +
=> {"hetfo"=>1, "vasarnap"=>7, "kedd"=>2}
 +
 
 +
 
 +
==== Iterátorok ====
 +
 
 +
A times iterátor:
 +
 
 +
>> print "Mondja! Maga "; 3.times {print "mindent ketszer mond? "}; print "\n"
 +
Mondja! Maga mindent ketszer mond? mindent ketszer mond? mindent ketszer mond?
 +
=> nil
 +
 
 +
>> a = "Mondja! Maga "
 +
=> "Mondja! Maga "
 +
>> 3.times {a << "mindent ketszer mond? "}
 +
=> 3
 +
>> a
 +
=> "Mondja! Maga mindent ketszer mond? mindent ketszer mond? mindent ketszer mond? "
 +
 
 +
Ciklusváltozó használatával:
 +
 
 +
>> 5.times {|i| print i}; print "\n"
 +
01234
 +
=> nil
 +
 
 +
Az upto, downto iterátor:
 +
 
 +
>> 0.upto(4) {|i| print i}; print "\n"
 +
01234
 +
=> nil
 +
>> 5.upto(8) {|i| print i}; print "\n"
 +
5678
 +
=> nil
 +
>> 8.downto(5) {|i| print i}; print "\n"
 +
8765
 +
=> nil
 +
 
 +
==== break, next ====
 +
 
 +
while i<6
 +
  if i == 4 then break end
 +
  print i
 +
  i += 1
 +
end
 +
 
 +
0.upto(5) do |i|
 +
  if i == 4 then break 17 end  # visszatérési érték is megadható, egyébként nil
 +
  print i
 +
  i += 1
 +
end
 +
 
 +
 
 +
 
 +
 
 +
==== Megszámlálható objektumok ====
 +
 
 +
Megszámlálható objektumok (''enumerable objects''). Amely osztályok objektumaira definiálva van az each metódus. Pl. Array, Hash, Range.
 +
 
 +
Az each, map és inject iterátorok használata:
 +
 
 +
>> [1,2,3].each {|i| p i}
 +
1
 +
2
 +
3
 +
=> [1, 2, 3]
 +
>> [1,2,3].map {|i| i**2}
 +
=> [1, 4, 9]
 +
>> [1,2,3].inject {|sum, i| sum + i}
 +
=> 6
 +
 
 +
>> (1..4).each {|i| print i}
 +
1234=> 1..4
 +
 
 +
>> (1..4).map {|i| i**2}
 +
=> [1, 4, 9, 16]
 +
 
 +
>> (1..4).inject {|s,i| s+i}
 +
=> 10
 +
 
 +
Dobjuk fel 4-szer a kockát, és nézzük meg, melyik a legnagyobb dobás:
 +
 
 +
>> a = Array.new(4) {rand(6) + 1}
 +
=> [3, 6, 6, 4]
 +
>> a.inject {|m,i| m > i ? m : i}
 +
=> 6
 +
 
 +
sőt!
 +
 
 +
(Array.new(4) {rand(6) + 1}).inject {|m,i| m > i ? m : i}
 +
 
 +
A következő program egy fájlt kiír az std outputra:
 +
 
 +
File.open(filenev) do |f|
 +
  f.each {|sor| print sor }
 +
end
 +
 
 +
Az each_with_index használata (az előző példa sorszámozott sorokkal):
 +
 
 +
>> filenev = "rrrr"
 +
=> "rrrr"
 +
>> File.open(filenev) do |f|
 +
?>  f.each_with_index do |sor,i|
 +
?>    print "#{i+1}: #{sor}"
 +
>>  end
 +
>> end
 +
1: Ez egy tesztfile
 +
2: Ez a 2. sora
 +
3: Ez meg az utso
 +
=> #<File:rrrr (closed)>
 +
 
 +
=== Blokkok ===
 +
 
 +
Blokkok csak metódushívásokat követhetnek. Vagy kapcsos zárójelek közt vagy do és end közt adhatók meg.
 +
 
 +
>> h = {:piros => 0xff0000, :zold => 0x00ff00, :kek => 0x0000ff}
 +
=> {:piros=>16711680, :zold=>65280, :kek=>255}
 +
>> h.each {|kulcs, ertek| print "%s kodja " % kulcs, "%06x\n" % ertek}
 +
piros kodja ff0000
 +
zold kodja 00ff00
 +
kek kodja 0000ff
 +
=> {:piros=>16711680, :zold=>65280, :kek=>255}
 +
 
 +
==== redo/retry ====
 +
 
 +
 
 +
A redo csak azt a ciklust ismétli
 +
puts "Mi jut eszedbe?"
 +
szavak = %w(alma piros tehen)
 +
valasz = szavak.collect do |szo|
 +
  print szo + "> "
 +
  valasz = gets.chop
 +
  if valasz.size == 0
 +
    szo.upcase!
 +
    redo
 +
  end
 +
  valasz
 +
end
 +
 
 +
A retry az egész ciklust ismétli
 +
n = 5
 +
n.times do |x|
 +
  print x
 +
  if x == n-1
 +
    n -= 1
 +
    retry
 +
  end
 +
end
 +
 
 +
==== catch/throw ====
 +
 
 +
a=[[1,nil,3],[2,3,4],[0,2,5]]
 +
catch :kiugrunk do
 +
  0.upto 2 do |i|
 +
    0.upto 2 do |j|
 +
      throw :kiugrunk unless a[i][j]
 +
      print a[i][j]
 +
    end
 +
    print "\n"
 +
  end
 +
end
 +
print "vege\n"
 +
 
 +
=== Példányváltozó -- setter ===
 +
 
 +
A példányváltozó beállítása az =-végű metódussal.
 +
(Az előadáson mutatott példában figyelmetlenségből nagy betűvel lett írva a class parancs!) Helyesen:
 +
 
 +
class Nev
 +
  attr_reader :vezetek, :kereszt
 +
 +
  def vezetek=(vezetek)
 +
    if vezetek == nil or vezetek.size == 0
 +
      raise ArgumentError.new('Mindenkinek van vezetekneve')
 +
    end
 +
    vezetek = vezetek.dup
 +
    vezetek[0] = vezetek[0].chr.capitalize
 +
    @vezetek = vezetek
 +
  end
 +
 +
  def kereszt=(kereszt)
 +
    if kereszt == nil or kereszt.size == 0
 +
      raise ArgumentError.new('Mindenkinek van keresztneve')
 +
    end
 +
    kereszt = kereszt.dup
 +
    kereszt[0] = kereszt[0].chr.capitalize
 +
    @kereszt = kereszt
 +
  end
 +
 +
  def teljes_nev
 +
    "#{@vezetek} #{@kereszt}"
 +
  end
 +
 +
  def initialize(vezetek, kereszt)
 +
    self.vezetek=vezetek
 +
    self.kereszt=kereszt
 +
  end
 +
end
 +
 
 +
=== Néhány példaprogram ===
 +
 
 +
Matmul Ruby nyelven:
 +
 
 +
a=[[1,2],                #megadjuk az 'a' mátrixot
 +
    [3,4],
 +
    [5,6]]
 +
 +
b=[[7],                  #megadjuk a 'b' mátrixot
 +
    [8]]
 +
 +
ab=[]                    #létrehozzuk az üres szorzatmátrixot
 +
i=0; while i<a.size;    #'' while'' ciklust nem véletlenül használunk, mivel Rubyban nincs ''for'' ciklus
 +
  ujsor=[]
 +
  j=0; while j<b[0].size;
 +
    x=0
 +
    k=0; while k<b.size
 +
      x+=a[i][k]*b[k][j]
 +
      ujsor[j]=x
 +
    end
 +
  ab[i]=ujsor
 +
end
 +
 
 +
Objektum-orientáltan kezdtünk programozni Ruby nyelven, a Sikidom, BBox és Kor osztályok kerültek fel a táblára.
 +
 
 +
class BBox
 +
  attr_accessor :llx, :lly, :urx, :ury
 +
end
 +
 +
class Sikidom
 +
  def bbox()
 +
    fail  # még nem tudjuk megírni, a Sikidom túl absztrakt
 +
  end
 +
  def kerulet()
 +
    fail  # még nem tudjuk megírni, a Sikidom túl absztrakt
 +
  end
 +
  def terulet()
 +
    fail  # még nem tudjuk megírni, a Sikidom túl absztrakt
 +
  end
 +
end
 +
 +
class Kor <Sikidom
 +
  attr_accessor :cx, :cy, :r
 +
  def bbox()
 +
    b=BBox.new
 +
    b.llx=@cx-@r; b.lly=@cy-@r
 +
    b.urx=@cx+@r; b.ury=@cy+@r
 +
    return b  # a return fölösleges
 +
  end
 +
  def kerulet()
 +
    Math::PI*2*@r
 +
  end
 +
  def terulet()
 +
    Math::PI*@r*@r
 +
  end
 +
end
 +
 
 +
Egy sudoku megoldás (Fischer Richárd megoldása alapján):
 +
 
 +
  # puts "Add meg a filenevet! "
 +
  # filenev=gets.chomp!
 +
  filenev = "sudoku1"
 +
  a=Array.new(9) {[]}
 +
  File.open(filenev) do |f|
 +
    f.each_with_index do |sor,i|
 +
      0.upto(8) {|j| a[i][j]= if sor[j]>48 then sor[j].chr.to_i else 0 end}
 +
    end
 +
  end
 +
 +
  vanuj=true
 +
  while vanuj do
 +
    vanuj=false
 +
    0.upto(8) do |i|
 +
      0.upto(8) do |j|
 +
        if a[i][j] == 0
 +
          d=(1..9).to_a
 +
          0.upto(8) do |k|
 +
            d.delete(a[i][k])
 +
            d.delete(a[k][j])
 +
          end
 +
          0.upto(2) do |e|
 +
            0.upto(2) do |f|
 +
              d.delete(a[i/3*3+e][j/3*3+f])
 +
            end
 +
          end 
 +
          if d.size==1
 +
            a[i][j]=d[0]
 +
            vanuj=true
 +
          end
 +
        end
 +
      end
 +
    end
 +
  end
 +
  0.upto(8) do |i|
 +
    0.upto(8) do |j|
 +
      print a[i][j]
 +
    end
 +
    print "\n"
 +
  end
 +
 
 +
Egy gráfokat kezelő osztály:
 +
 
 +
class Adjacencia < Array
 +
  attr_reader :adj
 +
  def initialize
 +
    @adj = []
 +
  end
 +
 +
  def [](x,y)
 +
    x,y = y,x if x > y
 +
    raise IndexError if x==y
 +
    @adj[ (y*y-y)/2 + x ]
 +
  end
 +
 +
  def []=(x,y,e)
 +
    x,y = y,x if x > y
 +
    raise IndexError if x==y
 +
    @adj[ (y*y-y)/2 + x ] = e
 +
  end
 +
end
 +
 +
class Graf
 +
  attr_reader :adj
 +
  def initialize( *elek )
 +
    @adj = Adjacencia.new
 +
    @csucsok = 0
 +
    for e in elek
 +
      @adj[e[0],e[1]] = 1
 +
      @csucsok = [@csucsok,e[0],e[1]].max
 +
    end
 +
  end
 +
 +
  def [](x,y)
 +
    @adj[x,y]
 +
  end
 +
 +
  def add x,y
 +
    @adj[x,y]=1
 +
    @csucsok = [@csucsok,x,y].max
 +
  end
 +
 +
  def fok(x)
 +
    (0..@csucsok).inject(0) {|s,i| x!=i && @adj[x,i] ? s+1 : s }
 +
  end
 +
 +
  def each_csucs
 +
    (0..@csucsok).each {|v| yield v}
 +
  end
 +
 +
  def each_el
 +
    for i in 0...@csucsok
 +
      for j in i+1..@csucsok
 +
        yield i,j if self[i,j]
 +
      end
 +
    end
 +
  end
 +
 +
  def osszefuggo?
 +
    c = @csucsok
 +
    volt = []
 +
    lesz = [c]
 +
    for i in 0...@csucsok
 +
      lesz << i if self[i,c]
 +
      volt << i if self[i,c]
 +
    end
 +
    while !volt.empty?
 +
      v = volt.shift
 +
      self.each_el do |x,y|
 +
        if x==v || y==v
 +
          z = x==v ? y : x
 +
          if !lesz.include? z
 +
            lesz << z
 +
            volt << z
 +
          end
 +
        end
 +
      end
 +
    end
 +
    lesz.size <= @csucsok ? false : true
 +
  end
 +
  def euler_kor?
 +
    return false if !osszefuggo?
 +
    paratlan = 0
 +
    each_csucs do |i|
 +
      if fok(i) % 2 == 1
 +
        paratlan += 1
 +
      end
 +
    end
 +
    paratlan == 0
 +
  end
 +
 +
  def euler_ut?
 +
    return false if !osszefuggo?
 +
    paratlan = 0
 +
    each_csucs do |i|
 +
      if fok(i) % 2 == 1
 +
        paratlan += 1
 +
      end
 +
    end
 +
    paratlan == 2
 +
  end
 +
 +
  def euler?
 +
    return false if !osszefuggo?
 +
    paratlan = 0
 +
    each_csucs do |i|
 +
      if fok(i) % 2 == 1
 +
        paratlan += 1
 +
      end
 +
    end
 +
    paratlan <= 2
 +
  end
 +
 +
  def show
 +
    (0...@csucsok).each do |i|
 +
      (0..@csucsok).each do |j|
 +
        print i>=j ? " " : (@adj[i,j] ? @adj[i,j] : 0)
 +
      end
 +
      print "\n"
 +
    end
 +
  end
 +
end

A lap jelenlegi, 2008. május 9., 17:53-kori változata

A tárgy főoldala: info2/2008tavasz A tárgy oktatásához felhasznált, http://wiki.math.bme.hu/ -n belüli wikioldalak GNU FDL licenc vagy (választás szerint) CC-BY-SA-2.0 licenc szerint szabadon használhatók és terjeszthetők.

Tartalomjegyzék

8. előadás (2007-04-04)

Objektum-orientált programozás

Néhány új fogalom:

  • osztály (a C-beli struktúratípusnak felel meg)
  • objektum (a C-beli struktúrának felel meg)
  • attribútum (a C-beli struktúramezőnek felel meg)
  • metódus (a C-beli függvénynek felel meg)

Osztály class (síkidom), osztálypéldány class instance = objektum object (kör, téglalap), konstruktor constructor (új objektum létrehozása), objektum azonosító (object identifier - object ID), példány változó instance variable (mely az objektum állapotát/tulajdonságait tartalmazza, pl. középpont), példány metódus instance method (),

Ruby nyelv

A Ruby interpretált, szkriptnyelv (interpretált, és minden rendszer szintű szolgáltatáshoz hozzá lehet férni).

A Ruby osztályhierarchiája hasonló a biológiai törzsfához. Például a gerincesek (osztály) törzsén belül a madarak (osztály) osztályának egy alosztálya a pacsirta (osztály) nevű faj, melynek egy példánya a Csipcsip nevű kismadarunk (objektum), aki nem mellesleg egy pacsirta. Őt jellemezhetjük különböző tulajdonságai alapján; ezek az attribútumok. Például: él-e még? , mennyire éhes, hogy hívják a párját, stb. Tehát bizonyos tulajdonságait megadjuk, amik csak rá jellemzőek.

                      Változók                                 Konstansok és
Lokális        Globális        Példány         Osztály         osztály nevek

valtozo        $valami         @nev            @@osztvalt      PI
joEjt_2        $_              @XY             @@N             String


Feltételes utasítások

if

Először then-nel, majd újsorral elválasztva:

if x==2 then x += 1 end
if x==2 
  x += 1 
end
if x==2; x += 1 end

else

if x==2 then x += 1 else x += 2 end
if x==2 
  x += 1 
else 
  x += 2
end

elsif

if x == 1
  "hetfo"
elsif x == 2
  "kedd"
elsif x == 3 then "szerda"
else "hetvege"
end

if kifejezésben

x = if y == 1
      3
    else
      4
    end
x = 4
x = 3 if y == 1

unless

unless x <= 4
  "nagyobb, mint 4"
end
unless x <= 4
  "nagyobb, mint 4"
else
  "nem nagyobb"
end
x = unless x <= 4 then 5 end
x = 5 unless x <= 4

case

A fenti if-es példával ekvivalens a következővel (a sorvége helyett itt is lehet then vagy pontosvessző):

case x
when 1
  "hetfo"
when 2
  "kedd"
when 3; "szerda"
else "hetvege"
end

Ekkor valójában a következő hajtódik végre (figyeljük meg a case-egyenlőség használatát):

case
when 1 === x then "hetfo"
when 2 === x then "kedd"
when 3 === x then "szerda"
else "hetvege"
end

Az === néha megegyezik ==-vel, pl. a Fixnum osztályban, más osztályokban külön definiálva van a case számára:

tartomány használata: itt 'a === b' azt jelenti, hogy 'b' benne van-e az 'a' tartományban:

case x
when 1..5
  "hetkoznap"
when 6..7
  "hetvege"
else "rossz adat"
end

reguláris kifejezés használata: itt 'a === b' azt jelenti, hogy 'b' illeszkedik-e az 'a' mintára:

x = "ab"
case x
when /[a-z]+/
  "szoveg"
when /[0-9]+/
  "szam"
end
=> "szoveg"

egy összetettebb példa (beolvassa és kiírja a sorokat addig, míg egy 'ennyi' tartalmút nem kap, a megjegyzés sorokat kihagyja):

while line=gets.chomp do
  case line
  when /^\s*#/
    next
  when /^ennyi$/i    # i = case insensitive
    break
  else
    puts line
  end
end

osztályhoz tartozás: a Class osztályban 'a === b' azt jelenti, hogy a 'b' az 'a' osttály példánya-e:

case x
when Numeric then "szam"
when String then "sztring"
else "valami mas"
end

A Ruby case utasításában nincs ráfutás, így nem is kell minden ágat lezárni, mint C-beli switch utasításban break-kel vagy return-nel!!!

 ?:

Ha x == 3, y legyen 4, egyébként 5:

x = y == 3 ? 4 : 5

Ciklus utasítások

while, until

>> x=0
=> 0
>> while x<4 do
?>   x += 1
>>   puts x
>> end
1
2
3
4
=> nil
>> x=0
=> 0
>> until x>=4 do
?>   x += 1
>>   puts x
>> end
1
2
3
4
=> nil

while, until kifejezésekben

>> x = 0; x += 1 while x<4; x
=> 4
>> x=0; x += 1 until x==4; x
=> 4

for-in ciklus

Egy tömbre és egy hash-táblára:

>> a = [1,2,3]
=> [1, 2, 3]
>> for i in a
>>   p i
>> end
1
2
3
=> [1, 2, 3]
>> h = {"hetfo"=>1, "kedd"=>2, "vasarnap"=>7}
=> {"hetfo"=>1, "vasarnap"=>7, "kedd"=>2}
>> for kulcs, ertek in h
>>   puts "#{kulcs} a(z) #{ertek}. nap"
>> end
hetfo a(z) 1. nap
vasarnap a(z) 7. nap
kedd a(z) 2. nap
=> {"hetfo"=>1, "vasarnap"=>7, "kedd"=>2}


Iterátorok

A times iterátor:

>> print "Mondja! Maga "; 3.times {print "mindent ketszer mond? "}; print "\n"
Mondja! Maga mindent ketszer mond? mindent ketszer mond? mindent ketszer mond?
=> nil
>> a = "Mondja! Maga "
=> "Mondja! Maga "
>> 3.times {a << "mindent ketszer mond? "}
=> 3
>> a
=> "Mondja! Maga mindent ketszer mond? mindent ketszer mond? mindent ketszer mond? "

Ciklusváltozó használatával:

>> 5.times {|i| print i}; print "\n"
01234
=> nil

Az upto, downto iterátor:

>> 0.upto(4) {|i| print i}; print "\n"
01234
=> nil
>> 5.upto(8) {|i| print i}; print "\n"
5678
=> nil
>> 8.downto(5) {|i| print i}; print "\n"
8765
=> nil

break, next

while i<6
  if i == 4 then break end
  print i
  i += 1
end
0.upto(5) do |i|
  if i == 4 then break 17 end  # visszatérési érték is megadható, egyébként nil
  print i
  i += 1
end



Megszámlálható objektumok

Megszámlálható objektumok (enumerable objects). Amely osztályok objektumaira definiálva van az each metódus. Pl. Array, Hash, Range.

Az each, map és inject iterátorok használata:

>> [1,2,3].each {|i| p i}
1
2
3
=> [1, 2, 3]
>> [1,2,3].map {|i| i**2}
=> [1, 4, 9]
>> [1,2,3].inject {|sum, i| sum + i}
=> 6
>> (1..4).each {|i| print i}
1234=> 1..4
>> (1..4).map {|i| i**2}
=> [1, 4, 9, 16]
>> (1..4).inject {|s,i| s+i}
=> 10

Dobjuk fel 4-szer a kockát, és nézzük meg, melyik a legnagyobb dobás:

>> a = Array.new(4) {rand(6) + 1}
=> [3, 6, 6, 4]
>> a.inject {|m,i| m > i ? m : i}
=> 6

sőt!

(Array.new(4) {rand(6) + 1}).inject {|m,i| m > i ? m : i}

A következő program egy fájlt kiír az std outputra:

File.open(filenev) do |f|
  f.each {|sor| print sor }
end

Az each_with_index használata (az előző példa sorszámozott sorokkal):

>> filenev = "rrrr"
=> "rrrr"
>> File.open(filenev) do |f|
?>   f.each_with_index do |sor,i|
?>     print "#{i+1}: #{sor}"
>>   end
>> end
1: Ez egy tesztfile
2: Ez a 2. sora
3: Ez meg az utso
=> #<File:rrrr (closed)>

Blokkok

Blokkok csak metódushívásokat követhetnek. Vagy kapcsos zárójelek közt vagy do és end közt adhatók meg.

>> h = {:piros => 0xff0000, :zold => 0x00ff00, :kek => 0x0000ff}
=> {:piros=>16711680, :zold=>65280, :kek=>255}
>> h.each {|kulcs, ertek| print "%s kodja " % kulcs, "%06x\n" % ertek}
piros kodja ff0000
zold kodja 00ff00
kek kodja 0000ff
=> {:piros=>16711680, :zold=>65280, :kek=>255}

redo/retry

A redo csak azt a ciklust ismétli

puts "Mi jut eszedbe?"
szavak = %w(alma piros tehen) 
valasz = szavak.collect do |szo|
  print szo + "> "
  valasz = gets.chop
  if valasz.size == 0
    szo.upcase!
    redo
  end
  valasz
end

A retry az egész ciklust ismétli

n = 5
n.times do |x|
  print x
  if x == n-1
    n -= 1
    retry
  end
end

catch/throw

a=[[1,nil,3],[2,3,4],[0,2,5]]
catch :kiugrunk do
  0.upto 2 do |i|
    0.upto 2 do |j|
      throw :kiugrunk unless a[i][j]
      print a[i][j]
    end
    print "\n"
  end
end
print "vege\n"

Példányváltozó -- setter

A példányváltozó beállítása az =-végű metódussal. (Az előadáson mutatott példában figyelmetlenségből nagy betűvel lett írva a class parancs!) Helyesen:

class Nev
  attr_reader :vezetek, :kereszt

  def vezetek=(vezetek)
    if vezetek == nil or vezetek.size == 0
      raise ArgumentError.new('Mindenkinek van vezetekneve')
    end
    vezetek = vezetek.dup
    vezetek[0] = vezetek[0].chr.capitalize
    @vezetek = vezetek
  end

  def kereszt=(kereszt)
    if kereszt == nil or kereszt.size == 0
      raise ArgumentError.new('Mindenkinek van keresztneve')
    end
    kereszt = kereszt.dup
    kereszt[0] = kereszt[0].chr.capitalize
    @kereszt = kereszt
  end

  def teljes_nev
    "#{@vezetek} #{@kereszt}"
  end

  def initialize(vezetek, kereszt)
    self.vezetek=vezetek
    self.kereszt=kereszt
  end
end

Néhány példaprogram

Matmul Ruby nyelven:

a=[[1,2],                #megadjuk az 'a' mátrixot
   [3,4],
   [5,6]]

b=[[7],                  #megadjuk a 'b' mátrixot
   [8]]

ab=[]                    #létrehozzuk az üres szorzatmátrixot
i=0; while i<a.size;     # while ciklust nem véletlenül használunk, mivel Rubyban nincs for ciklus
  ujsor=[]
  j=0; while j<b[0].size;
    x=0
    k=0; while k<b.size
      x+=a[i][k]*b[k][j]
      ujsor[j]=x
   end
 ab[i]=ujsor
end

Objektum-orientáltan kezdtünk programozni Ruby nyelven, a Sikidom, BBox és Kor osztályok kerültek fel a táblára.

class BBox
  attr_accessor :llx, :lly, :urx, :ury
end

class Sikidom
  def bbox()
    fail  # még nem tudjuk megírni, a Sikidom túl absztrakt
  end
  def kerulet()
    fail  # még nem tudjuk megírni, a Sikidom túl absztrakt
  end
  def terulet()
    fail  # még nem tudjuk megírni, a Sikidom túl absztrakt
  end
end

class Kor <Sikidom
  attr_accessor :cx, :cy, :r
  def bbox()
    b=BBox.new
    b.llx=@cx-@r; b.lly=@cy-@r
    b.urx=@cx+@r; b.ury=@cy+@r
    return b  # a return fölösleges
  end
  def kerulet()
    Math::PI*2*@r
  end
  def terulet()
    Math::PI*@r*@r
  end
end

Egy sudoku megoldás (Fischer Richárd megoldása alapján):

 # puts "Add meg a filenevet! "
 # filenev=gets.chomp!
 filenev = "sudoku1"
 a=Array.new(9) {[]}
 File.open(filenev) do |f|
   f.each_with_index do |sor,i|
     0.upto(8) {|j| a[i][j]= if sor[j]>48 then sor[j].chr.to_i else 0 end}
   end
 end

 vanuj=true
 while vanuj do
   vanuj=false
   0.upto(8) do |i|
     0.upto(8) do |j|
       if a[i][j] == 0
         d=(1..9).to_a
         0.upto(8) do |k|
           d.delete(a[i][k])
           d.delete(a[k][j])
         end
         0.upto(2) do |e|
           0.upto(2) do |f|
             d.delete(a[i/3*3+e][j/3*3+f])
           end
         end  
         if d.size==1 
           a[i][j]=d[0]
           vanuj=true 
         end
       end
     end
   end
 end
 0.upto(8) do |i|
   0.upto(8) do |j|
     print a[i][j]
   end
   print "\n"
 end

Egy gráfokat kezelő osztály:

class Adjacencia < Array
  attr_reader :adj
  def initialize
    @adj = []
  end

  def [](x,y)
    x,y = y,x if x > y
    raise IndexError if x==y
    @adj[ (y*y-y)/2 + x ]
  end

  def []=(x,y,e)
    x,y = y,x if x > y
    raise IndexError if x==y
    @adj[ (y*y-y)/2 + x ] = e
  end
end

class Graf
  attr_reader :adj
  def initialize( *elek )
    @adj = Adjacencia.new
    @csucsok = 0
    for e in elek
      @adj[e[0],e[1]] = 1
      @csucsok = [@csucsok,e[0],e[1]].max
    end
  end

  def [](x,y)
    @adj[x,y]
  end

  def add x,y
    @adj[x,y]=1
    @csucsok = [@csucsok,x,y].max
  end

  def fok(x)
    (0..@csucsok).inject(0) {|s,i| x!=i && @adj[x,i] ? s+1 : s }
  end

  def each_csucs
    (0..@csucsok).each {|v| yield v}
  end

  def each_el
    for i in 0...@csucsok
      for j in i+1..@csucsok
        yield i,j if self[i,j]
      end
    end
  end

  def osszefuggo?
    c = @csucsok
    volt = []
    lesz = [c]
    for i in 0...@csucsok
      lesz << i if self[i,c]
      volt << i if self[i,c]
    end
    while !volt.empty?
      v = volt.shift
      self.each_el do |x,y|
        if x==v || y==v
          z = x==v ? y : x
          if !lesz.include? z
            lesz << z
            volt << z
          end
        end
      end
    end
    lesz.size <= @csucsok ? false : true
  end
  def euler_kor?
    return false if !osszefuggo?
    paratlan = 0
    each_csucs do |i|
      if fok(i) % 2 == 1
        paratlan += 1
      end
    end
    paratlan == 0
  end

  def euler_ut?
    return false if !osszefuggo?
    paratlan = 0
    each_csucs do |i|
      if fok(i) % 2 == 1
        paratlan += 1
      end
    end
    paratlan == 2
  end

  def euler?
    return false if !osszefuggo?
    paratlan = 0
    each_csucs do |i|
      if fok(i) % 2 == 1
        paratlan += 1
      end
    end
    paratlan <= 2
  end

  def show
    (0...@csucsok).each do |i|
      (0..@csucsok).each do |j|
        print i>=j ? " " : (@adj[i,j] ? @adj[i,j] : 0)
      end
      print "\n"
    end
  end
end
Személyes eszközök