SZIMP2 Ruby
A tárgy főoldala: SZIMP2
8. előadás (2007-04-06)
Az objektum-orientált programozás elméleti bevezetője hangzott el, és a matmul feladat megoldása felkerült a táblára Ruby nyelven.
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)
Ha 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.
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
9. gyakorlat (2007-04-10, 2007-04-13)
lepcso.rb:
n=7 l=[[1], [2,3], [4,5,6], ... ] # l.size==n p l
9. előadás (2007-04-13)
Objektum-orientáltan kezdtünk programozni Ruby nyelven, a Sikidom, BBox és Kor osztályok kerültek fel a táblára. Megismertük a szemétgyűjtést (garbage collection) is. Láttunk példát rá, hogy két változó mutathat ugyanarra az objektumra.
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
A szemétgyűjtő kipucolja a felesleges dolgokat a memóriából. A először mindent lenulláz, azután pedig akire van mutató egy elérhető helyről, azt megnöveli. Ha két szemét egymásra mutat, akkor egyiküket sem növeli, mert egymásból ugyan elérhetőekm de ők maguk nem érhetőek el egy elérhető helyről. A szemétgyőjtés alatt a program lelassul.
A szemétgyűjtés szemléltetése egy példában:
class Gyerek #létrehozunk egy Gyerek osztályt attr_accessor :nev, :kov #az elemei névvel(nev) és egy mutatóval(kov) fognak rendelkezni def initialize(nev) @nev=nev; @kov=nil; end #definiáljuk a konstruktort, amely egy nevet fog kérni end def egyik() #definiáljuk az egyik metódust pal=Gyerek.new("Pal") #pal legyen a Gyerek osztály egy új példánya kata=Gyerek.new("Kata") peter=Gyerek.new("Peter") pal.kov=kata; kata.kov=peter; peter.kov=pal #létrehozzuk a mutatókat a csoporbeli viszonyok definiálásához end def masik() sandor=Gyerek.new("Sandor") jozsef=Gyerek.new("Jozsef") benedek=Gyerek.new("Benedek") sandor.kov=jozsef; jozsef.kov=benedek end a=egyik b=masik # itt a=nil
Ha a szemétgyűjtés #itt megy végbe, akkor a következő történik:
Csak azok maradnak meg, akik ekkor az a és a b változókból valamilyen úton elérhetők. Az a változó ekkor az egyik metódus utoljára használt elemére (objektumára) fog mutatni. (A mi esetünkben Pálra.) Pál, Kata és Péter egy ciklikus csoportot alkotnak, tehát bármelyikükből bármelyikük elérhető, így az 'a' változóból is. Ezért a szemétgyűjtés során, egyik kis barátunknak sem fog baja esni. A b változó a fent leírtak alapján Benedekre fog mutatni. De Sándor csak Józsefre, József csak Benedekre mutat, más viszony nem áll fent a csoportban. Így a b változóból CSAK Benedek érhetó el, így a szemétgyűjtés során Sándortól és Józseftől sajnos meg kell válnunk. Még akkor is, ha Sándorból József elérhető, hiszen itt egy szemét mutat egy másik szemétre. (Sándortól és Józseftől egyben elnézést is kérünk a kifejezésért!)
10. gyakorlat (2007-04-17, 2007-04-20)
A Szórt Spektrum a Háztetőn Csillagjós és Rádióamatőr Klub holnap Guinness-rekordkísérletre készül: annyi űrturistával kívánják felvenni a kapcsolatot egy nap alatt, amennyivel csak lehet. Az internetről letöltötték a holnapi űrturista-áthaladási időpontokat. Minden áthaladáshoz három adat tartozik: az űrturista neve (szóközt nem tartalmaz), mikortól (hány órától) kezdve tartózkodik a klub rádiójának vételkörzetében, és mikor hagyja el a vételkörzetet. E két időpont egy balról zárt, jobbról nyílt intervallum végpontjai. Példa áthaladási időpontok (~/szimp2/urturista1.in):
Pirx_pilota 5 7 Leia_hercegno 7 9 Dalek_42 6 10 Kis_herceg 1 6
Írjon programot Ruby nyelven, ami beolvassa az áthaladási időpontokat, és kiválaszt a benne levő űrturisták közül egy olyan maximális részhalmazt, melynek bármely két eleméhez tartozó intervallumok diszjunktak. (A diszjunktságot azért kell kikötni, mert a klubnak csak egy rádiója van, és szerencsétlen esetben előfordulhat, hogy a teljes intervallumban próbálkozniuk kell, hogy végre összejöjjön a kapcsolat.)
Ötlet: Használja ki, hogy ha az intervallumok végpont szerint növekvő sorrendbe vannak rendezve, akkor a mohó algoritmus jó és optimális eredményt ad. A mohó algoritmus veszi sorra az intervallumokat, és egy intervallumot akkor választ ki, ha az ő metszete az utoljára kiválasztott intervallummal üres halmaz.
Mintamegoldás (Ivett és Viktor, ~/szimp2/urturista.rb):
class Intervallum attr_accessor :nev, :a, :b end p=[] STDIN.each_line { |s| t=s.split(/\s+/) h=Intervallum.new h.nev=t[0] h.a=t[1].to_i h.b=t[2].to_i #p[p.size]=h #p.push(h) p << h } p.sort!{|ha,hb| ha.b<=>hb.b} q=[] h0=nil p.each { |h| if (h0==nil || [h.a,h0.a].max >= [h.b,h0.b].min); q.push(h) h0=h end } q.each { |h| STDOUT.puts h.nev }
10. előadás (2007-04-20)
Megtanultuk:
- konstruktor (initialize)
- példányváltozó (@)
- metódus, amely beállít egy példányváltozót (attr_writer)
- metódus, amely visszaad egy példányváltozót (attr_reader)
- fekete doboz
11. gyakorlat (2007-04-24, 2007-04-27)
A keddi gyakorlat sportnap miatt elmaradt.
A gyakorlatot Bácsi László, LacKac (e-mail: lackac@) tartotta stringfeldolgozásról. Ezt a programot (~lackac/ruby/backup.rb) magyarázta:
require 'net/http' require 'iconv' class String def to_latin2 Iconv.iconv('iso-8859-2', 'utf-8', self)[0] end end class Feed def initialize url @url = url @items = [] end def get_content @content = Net::HTTP.get(URI.parse(@url)) end def parse get_content @content.gsub(/<item.*?>.*?<\/item>/m) do |match| item = {} match =~ /<title>(<!\[CDATA\[)?(.*?)(\]\]>)?<\/title>/m item[:title] = $2 match =~ /<link>(.*?)<\/link>/m item[:link] = $1 match =~ /<description>(<!\[CDATA\[)?(.*?)(\]\]>)?<\/description>/m item[:desc] = $2 @items << item end end def parse_by_xml require 'rexml/document' get_content xml = REXML::Document.new(@content) xml.elements.each('//item') do |match| item = {} item[:title] = match.elements['title'].text item[:link] = match.elements['link'].text item[:desc] = match.elements['description'].text @items << item end end def list @items.each do |item| puts item[:title].to_latin2 puts item[:link].to_latin2 l=0 item[:desc].split.each do |w| print " " if l == 0 print w.to_latin2+" " l+=w.length+1 if l > 60 print "\n" l = 0 end end print "\n" end end end feed = Feed.new(ARGV[0]) feed.parse_by_xml feed.list
Így próbáltuk:
$ ruby backup.rb 'http://24ora.index.hu/?rss&keres=&szerzokeres=&rovatkeres=osszes&order=1' $ ruby backup.rb 'http://origo.hu/contentpartner/rss/hircentrum/origo.xml'
11. előadás (2007-04-27)
Az előadást Bácsi László, LacKac (e-mail: lackac@) tartotta stringfeldolgozásról. Az IRB-be ezt írta be:
$ irb irb(main):001:0> irb(main):002:0* irb(main):003:0* n=42 => 42 irb(main):004:0> n.class => Fixnum irb(main):005:0> f=3.14 => 3.14 irb(main):006:0> f.class => Float irb(main):007:0> s="alma" => "alma" irb(main):008:0> s.class => String irb(main):009:0> irb(main):010:0* irb(main):011:0* irb(main):012:0* s.methods => ["methods", "instance_eval", "%", "rindex", "map", "<<", "split", "any?", "dup", "sort ", "strip", "size", "instance_variables", "downcase", "min", "gsub!", "count", "include?" , "succ!", "instance_of?", "extend", "downcase!", "intern", "squeeze!", "eql?", "*", "nex t", "find_all", "each", "rstrip!", "each_line", "+", "id", "sub", "slice!", "hash", "sing leton_methods", "tr", "replace", "inject", "reverse", "taint", "unpack", "sort_by", "lstr ip", "frozen?", "instance_variable_get", "capitalize", "max", "chop!", "method", "kind_of ?", "capitalize!", "scan", "select", "to_a", "display", "each_byte", "type", "casecmp", " gsub", "protected_methods", "empty?", "to_str", "partition", "tr_s", "tr!", "match", "gre p", "rstrip", "to_sym", "instance_variable_set", "next!", "swapcase", "chomp!", "is_a?", "swapcase!", "ljust", "respond_to?", "between?", "reject", "to_s", "upto", "hex", "sum", "class", "object_id", "reverse!", "chop", "<=>", "insert", "<", "tainted?", "private_meth ods", "==", "delete", "dump", "===", "__id__", "member?", "tr_s!", ">", "concat", "nil?", "untaint", "succ", "find", "strip!", "each_with_index", ">=", "to_i", "rjust", "<=", "se nd", "index", "collect", "inspect", "slice", "oct", "all?", "clone", "length", "entries", "chomp", "=~", "public_methods", "upcase", "sub!", "squeeze", "__send__", "upcase!", "cr ypt", "delete!", "equal?", "freeze", "detect", "zip", "[]", "lstrip!", "center", "[]=", " to_f"] irb(main):013:0> s+" korte" => "alma korte" irb(main):014:0> s => "alma" irb(main):015:0> s.methods.class => Array irb(main):016:0> s.methods.find_all {|m| m =~ /case/} => ["downcase", "downcase!", "casecmp", "swapcase", "swapcase!", "upcase", "upcase!"] irb(main):017:0> irb(main):018:0* irb(main):019:0* irb(main):020:0* a=[1,2,3,4,5,6] => [1, 2, 3, 4, 5, 6] irb(main):021:0> a.clas NoMethodError: undefined method `clas' for [1, 2, 3, 4, 5, 6]:Array from (irb):21 from :0 irb(main):022:0> a.class => Array irb(main):023:0> a.methods => ["methods", "instance_eval", "rindex", "map", "<<", "any?", "&", "dup", "sort", "join" , "pack", "reverse_each", "size", "instance_variables", "min", "include?", "collect!", "r assoc", "instance_of?", "extend", "at", "compact!", "eql?", "*", "find_all", "each", "+", "reject!", "flatten", "slice!", "pop", "hash", "id", "singleton_methods", "-", "transpos e", "replace", "inject", "reverse", "indexes", "sort_by", "taint", "map!", "uniq", "max", "frozen?", "instance_variable_get", "fetch", "method", "kind_of?", "values_at", "|", "se lect", "to_a", "display", "type", "shift", "protected_methods", "empty?", "clear", "parti tion", "indices", "grep", "instance_variable_set", "is_a?", "flatten!", "first", "respond _to?", "reject", "to_s", "delete_at", "nitems", "class", "reverse!", "<=>", "unshift", "i nsert", "object_id", "tainted?", "private_methods", "==", "delete", "fill", "===", "__id_ _", "member?", "uniq!", "concat", "find", "nil?", "untaint", "compact", "each_with_index" , "last", "send", "delete_if", "index", "collect", "inspect", "slice", "all?", "clone", " =~", "each_index", "length", "entries", "sort!", "assoc", "public_methods", "__send__", " to_ary", "equal?", "freeze", "detect", "zip", "[]", "[]=", "push"] irb(main):024:0> n.size => 4 irb(main):025:0> a.size => 6 irb(main):026:0> a.nitems => 6 irb(main):027:0> n => 42 irb(main):028:0> irb(main):029:0* irb(main):030:0* a=[1,2,3,4] => [1, 2, 3, 4] irb(main):031:0> b=["egy","ketto",harom"] irb(main):032:1" " irb(main):033:1> b=["egy","ketto","harom"] irb(main):034:1> 10000000.size irb(main):035:1> 10_000_000.size irb(main):036:1> " irb(main):037:1" " irb(main):038:1> irb(main):039:1* irb(main):040:1* irb(main):041:1* n=10_000_000_000 irb(main):042:1> n.class irb(main):043:1> SyntaxError: compile error (irb):31: parse error, unexpected tSTRING_BEG, expecting kDO or '{' or '(' b=["egy","ketto",harom"] ^ from (irb):42 from (null):0 LacKac@LacKacMB:~> irb irb(main):001:0> n=10_000_000_000 => 10000000000 irb(main):002:0> n.size => 8 irb(main):003:0> n=1_000_000_000 => 1000000000 irb(main):004:0> n.size => 4 irb(main):005:0> a=[1,2,3,4] => [1, 2, 3, 4] irb(main):006:0> b=["egy","ketto","harom"] => ["egy", "ketto", "harom"] irb(main):007:0> a+b => [1, 2, 3, 4, "egy", "ketto", "harom"] irb(main):008:0> [1,2,3]+[3,4,5] => [1, 2, 3, 3, 4, 5] irb(main):009:0> [1,2,3]|[3,4,5] => [1, 2, 3, 4, 5] irb(main):010:0> [1,2,3]-[3,4,5] => [1, 2] irb(main):011:0> [1,2,3]^[3,4,5] NoMethodError: undefined method `^' for [1, 2, 3]:Array from (irb):11 from :0 irb(main):012:0> [1,2,3]&[3,4,5] => [3] irb(main):013:0> [1,2,3]*3 => [1, 2, 3, 1, 2, 3, 1, 2, 3] irb(main):014:0> [1,2,3]==[1,2,3] => true irb(main):015:0> [1,2,3]==[2,1,3] => false irb(main):016:0> a=[1,2,3,4,5] => [1, 2, 3, 4, 5] irb(main):017:0> a[0] => 1 irb(main):018:0> a[4] => 5 irb(main):019:0> a[-1] => 5 irb(main):020:0> a[-3] => 3 irb(main):021:0> a[-3]="harom" => "harom" irb(main):022:0> a => [1, 2, "harom", 4, 5] irb(main):023:0> a[23423] => nil irb(main):024:0> a.sort ArgumentError: comparison of Fixnum with String failed from (irb):24:in `sort' from (irb):24 from :0 irb(main):025:0> a.sort_by^C irb(main):025:0> a=[3,6,2,89,42 irb(main):026:1> ] => [3, 6, 2, 89, 42] irb(main):027:0> a=[3,6,2,89,42] => [3, 6, 2, 89, 42] irb(main):028:0> a.sort => [2, 3, 6, 42, 89] irb(main):029:0> b => ["egy", "ketto", "harom"] irb(main):030:0> b.sort => ["egy", "harom", "ketto"] irb(main):031:0> c=a.map! {|i| i.to_s} => ["3", "6", "2", "89", "42"] irb(main):032:0> a.has?(4) NoMethodError: undefined method `has?' for ["3", "6", "2", "89", "42"]:Array from (irb):32 from :0 irb(main):033:0> a.include?(4) => false irb(main):034:0> a.include?(89) => false irb(main):035:0> a => ["3", "6", "2", "89", "42"] irb(main):036:0> a.include?("89") => true irb(main):037:0> a.sort => ["2", "3", "42", "6", "89"] irb(main):038:0> a.sort_by {|i| i.to_i} => ["2", "3", "6", "42", "89"] irb(main):039:0> a => ["3", "6", "2", "89", "42"] irb(main):040:0> b => ["egy", "ketto", "harom"] irb(main):041:0> b=["alma","korte","dio"] => ["alma", "korte", "dio"] irb(main):042:0> b.sort_by {|s| s.length} => ["dio", "alma", "korte"] irb(main):043:0> b.sort_by {|valami| valami.length} => ["dio", "alma", "korte"] irb(main):044:0> s="alma korte dio" => "alma korte dio" irb(main):045:0> s.length => 14 irb(main):046:0> s[6] => 111 irb(main):047:0> s[6].class => Fixnum irb(main):048:0> s[6] == "a" => false irb(main):049:0> s[6] == "k" => false irb(main):050:0> s[6] == ?k => false irb(main):051:0> ?k => 107 irb(main):052:0> ?o => 111 irb(main):053:0> s[6] == ?o => true irb(main):054:0> irb(main):055:0* s => "alma korte dio" irb(main):056:0> s.split => ["alma", "korte", "dio"] irb(main):057:0> mac="16:ef:3d:3a:56:dc" => "16:ef:3d:3a:56:dc" irb(main):058:0> mac.split(":") => ["16", "ef", "3d", "3a", "56", "dc"] irb(main):059:0> mac="16:ef 3d.3a:56.dc" => "16:ef 3d.3a:56.dc" irb(main):060:0> mac.split(":") => ["16", "ef 3d.3a", "56.dc"] irb(main):061:0> mac.split(":. ") => ["16:ef 3d.3a:56.dc"] irb(main):062:0> mac.split(/[:\. ]/) => ["16", "ef", "3d", "3a", "56", "dc"] irb(main):063:0> "alma"+"korte" => "almakorte" irb(main):064:0> "alma"+" "+"korte" => "alma korte" irb(main):065:0> 'alma' => "alma" irb(main):066:0> print "alma\n" alma => nil irb(main):067:0> print "alma" alma=> nil irb(main):068:0> print "alma\n" alma => nil irb(main):069:0> print "alma\tkorte\tdio\n" alma korte dio => nil irb(main):070:0> print 'alma\tkorte\tdio\n' alma\tkorte\tdio\n=> nil irb(main):071:0> irb(main):072:0* n=42 => 42 irb(main):073:0> s="egy" => "egy" irb(main):074:0> s="ertelme" => "ertelme" irb(main):075:0> print "az elet "+s+" "n SyntaxError: compile error (irb):75: parse error, unexpected tIDENTIFIER, expecting $ from (irb):75 from :0 irb(main):076:0> print "az elet "+s+" "+n TypeError: can't convert Fixnum into String from (irb):76:in `+' from (irb):76 from :0 irb(main):077:0> print "az elet "+s+" "+n.to_s az elet ertelme 42=> nil irb(main):078:0> print "az elet "+s+" "+n.to_s+"\n" az elet ertelme 42 => nil irb(main):079:0> print "az elet #{s} #{n}\n" az elet ertelme 42 => nil irb(main):080:0> print "az elet #{s.upcase} #{n}\n" az elet ERTELME 42 => nil irb(main):081:0> irb(main):082:0* irb(main):083:0* irb(main):084:0* s.upcase => "ERTELME" irb(main):085:0> s="LacKac" => "LacKac" irb(main):086:0> s.upcase => "LACKAC" irb(main):087:0> s.downcase => "lackac" irb(main):088:0> s.swapcase => "lACkAC" irb(main):089:0> s => "LacKac" irb(main):090:0> s.upcase! => "LACKAC" irb(main):091:0> s => "LACKAC" irb(main):092:0> s.methods => ["methods", "instance_eval", "%", "rindex", "map", "<<", "split", "any?", "dup", "sort ", "strip", "size", "instance_variables", "downcase", "min", "gsub!", "count", "include?" , "succ!", "instance_of?", "extend", "downcase!", "intern", "squeeze!", "eql?", "*", "nex t", "find_all", "each", "rstrip!", "each_line", "+", "id", "sub", "slice!", "hash", "sing leton_methods", "tr", "replace", "inject", "reverse", "taint", "unpack", "sort_by", "lstr ip", "frozen?", "instance_variable_get", "capitalize", "max", "chop!", "method", "kind_of ?", "capitalize!", "scan", "select", "to_a", "display", "each_byte", "type", "casecmp", " gsub", "protected_methods", "empty?", "to_str", "partition", "tr_s", "tr!", "match", "gre p", "rstrip", "to_sym", "instance_variable_set", "next!", "swapcase", "chomp!", "is_a?", "swapcase!", "ljust", "respond_to?", "between?", "reject", "to_s", "upto", "hex", "sum", "class", "object_id", "reverse!", "chop", "<=>", "insert", "<", "tainted?", "private_meth ods", "==", "delete", "dump", "===", "__id__", "member?", "tr_s!", ">", "concat", "nil?", "untaint", "succ", "find", "strip!", "each_with_index", ">=", "to_i", "rjust", "<=", "se nd", "index", "collect", "inspect", "slice", "oct", "all?", "clone", "length", "entries", "chomp", "=~", "public_methods", "upcase", "sub!", "squeeze", "__send__", "upcase!", "cr ypt", "delete!", "equal?", "freeze", "detect", "zip", "[]", "lstrip!", "center", "[]=", " to_f"] irb(main):093:0> s.reverse => "CAKCAL" irb(main):094:0> a => ["3", "6", "2", "89", "42"] irb(main):095:0> a.reverse => ["42", "89", "2", "6", "3"] irb(main):096:0> "bacsi laszlo".capitalize => "Bacsi laszlo" irb(main):097:0> nev = "bacsi laszlo istvan" => "bacsi laszlo istvan" irb(main):098:0> nev.splite NoMethodError: undefined method `splite' for "bacsi laszlo istvan":String from (irb):98 from :0 irb(main):099:0> nev.split => ["bacsi", "laszlo", "istvan"] irb(main):100:0> nev.split.map {|i| i.capitalize} => ["Bacsi", "Laszlo", "Istvan"] irb(main):101:0> nev.split.map {|i| i.capitalize}.join => "BacsiLaszloIstvan" irb(main):102:0> nev.split.map {|i| i.capitalize}.join(" ") => "Bacsi Laszlo Istvan" irb(main):103:0> a=["alma", "korte", "szilva"] => ["alma", "korte", "szilva"] irb(main):104:0> a.each {|i| puts i} alma korte szilva => ["alma", "korte", "szilva"] irb(main):105:0> a.each_with_index {|v,i| puts "#{i}: #{v}"} 0: alma 1: korte 2: szilva => ["alma", "korte", "szilva"] irb(main):106:0> a.each_with_index {|v,i| print "#{i}: #{v}"} 0: alma1: korte2: szilva=> ["alma", "korte", "szilva"] irb(main):107:0> a.each_with_index {|v,i| print "#{i}: #{v}\n"} 0: alma 1: korte 2: szilva => ["alma", "korte", "szilva"] irb(main):108:0> s="alma\nkorte\nszilva" => "alma\nkorte\nszilva" irb(main):109:0> print s alma korte szilva=> nil irb(main):110:0> s.each_line {|l| puts l} alma korte szilva => "alma\nkorte\nszilva" irb(main):111:0> irb(main):112:0* irb(main):113:0* irb(main):114:0* s => "alma\nkorte\nszilva" irb(main):115:0> s="alma korte szilva" => "alma korte szilva" irb(main):116:0> s.split => ["alma", "korte", "szilva"] irb(main):117:0> a=["alma", "korte", szilva"] irb(main):118:1" ["] SyntaxError: compile error (irb):117: parse error, unexpected tSTRING_BEG, expecting kDO or '{' or '(' a=["alma", "korte", szilva"] ^ (irb):118: parse error, unexpected ']', expecting $ from (irb):118 from :0 irb(main):119:0> a=["alma", "korte", "szilva"] => ["alma", "korte", "szilva"] irb(main):120:0> a= %w{ alma korte szilva } => ["alma", "korte", "szilva"] irb(main):121:0> a= %w{alma korte szilva} => ["alma", "korte", "szilva"]
12. gyakorlat (2007-05-01, 2007-05-04)
A keddi, május elsejei gyakorlat az ünnep miatt elmaradt. Énekszó és tánc köszöntse. A pénteki gyakorlat anyaga következik.
A tanult C beépített függvények:
- printf()
- scanf()
- putchar()
- getchar()
- abort()
A tanult Ruby beépített osztályok:
- Object
- Float
- Fixnum
- Bignum
- Integer
- String
- Array
- Module
- Class
- TrueClass
- FalseClass
- NilClass
- Hash
- Regexp
Próbálkozások:
$ irb 1.2.class 42.class 42.class.class 42.class.class.class 2**42.class # hiba (2**42).class (2**61).class (2**62).class "".class .class [].class {}.class 42.class.superclass 42.class.superclass.superclass 42.class.superclass.superclass.superclass 42.kind_of?(Fixnum) #: true 42.kind_of?(Integer) #: true 42.kind_of?(String) #: false true.class false.lass nil.class Float.superclass String.methods
Az összes beépített osztály listázása (pl. String):
Object.constants.sort.select { |c| Object.const_get(c).kind_of?(Class) }
Az összes beépített modul listázása (pl. Math):
Object.constants.sort.select { |c| Object.const_get(c).kind_of?(Module) && !Object.const_get(c).kind_of?(Class) }
Osztályhiearachia-részlet:
Object String Array Hash Regexp Numeric Float Integer Fixnum Bignum
A program (listastszamol.rb), amely megszámolja, hogy az aktuális felhasználó levelesládájában a mat06 levelezési listára ki hány levelet küldött, és a küldések számának növekvő sorrendjében nyomtatja ki a feladó e-mail címeket:
r=/^List-Id: *mat06[.]lists[.]math[.]bme[.]hu$/ # p r.class #: Regexp f=File.open(ENV["MAIL"]) listas=false; fejlecben=false froml=nil h={} f.each_line { |l| if fejlecben && l =~ r; then listas=true end if fejlecben && l =~ /^From: *(.*)/ froml=$1 if froml=~/<(.*?)>/; froml=$1 end if froml !~/[@]/; froml=nil end end if fejlecben && l =~ /^$/; if listas and froml; if h.has_key?(froml); h[froml]+=1 else h[froml]=1 end #p froml end froml=nil; listas=false; fejlecben=false end if l =~ /^From /; fejlecben=true end } h.sort { |a,b| a[1]<=>b[1] }.each { |k,v| print "#{v} #{k}\n" }
12. előadás (2007-05-04)
Kivételkezelésről volt szó.
begin f=File.open("bableves.txt") p f.readline p f.readline rescue Errno::ENOENT print "A fájl nem létezik!\n" rescue SystemCallError => e print "Rendszerhiba\n" p e ensure f.close if f && !f.closed? end
Osztályhiererchia-részlet:
Object Exception StandardError SystemCallError Errno::ENOENT
Saját kivétel definiálása:
class NemPozitivHiba <Exception; end def feldolgoz(sor) f=sor.to_f raise NemPozitivHiba, "valami üzenet" if f<=0.0 print "#{Math::sqrt(f)}\n" end STDIN.each_line { |l| begin feldolgoz(l) rescue NemPozitivHiba print "NEM POZITIV\n" end }
13. gyakorlat (2007-05-08, 2007-05-11)
Ezen a pénteken volt a ZH. A gyakorlaton a ZH-feladatokhoz hasonló feladatokat oldottunk meg.
13. előadás (2007-05-11)
A gyors prímtényezőkre bontás algoritmusa volt. Pontosabban: egy olyan algoritmus hangzott el, ami egy j jegyű pozitív egész számnak polinomidőben (O(j^k) db alapművelettel) meghatározza egy valódi osztóját, vagy visszaadja, hogy a szám prím. Sajnos az algoritmus a részletszámítások során az eredeti számnál jóval többjegyű számokkal végez műveleteket, és mivel nagy számokkal lassú számolni, ezért hiába végez kevés műveletet, összességében lassú lesz, legalábbis a mai számítógépeken polinomidőnél lassabb lesz.
14. gyakorlat (2007-05-15, 2007-05-18)
A terv az volt, hogy nyomkövetésről és hibakeresésről lesz szó mind C, mind Ruby nyelven. printf, kdevelop, valgrind, strace.
Ezek közül megvalósítottuk a hibakeresést C nyelven printf és valgrind segítségével, továbbá volt szó a veremkialakításról C-ben, a puffertúlcsordulásos hibákról és kihasználásukról.
Mi a hiba az alábbi C programban? ~/szimp2/fibjavit0.c:
#include <stdio.h> int fib(int n) { printf("FIB %d\n", n); if (n==0) return 0; return fib(n-1, t)+fib(n-2); }
int main(void) { printf("%d\n", fib(10)); return 0; }
Mi a hiba az alábbi C programban? ~/szimp2/fibjavit1.c:
#include <stdio.h> int fib(int n, int *t) { int f; if (n<2) f=n; else f=fib(n-1, t)+fib(n-2, t); t[n]=f; return f; }
int main(void) { int t[2]; printf("%d\n", fib(10, &t[0])); return 0; }
Mi a hiba az alábbi C programban? ~/szimp2/fibjavit2.c:
#include <stdio.h> #include <stdlib.h> int fib(int n, int *t) { int i; t[0]=0; if (n<=0) return t[0]; t[1]=1; if (n<=1) return t[1]; for (i=2;i<=n;i++) { t[i]=t[i-1]+t[i-2]; } return t[n]; } int main(void) { int *t=malloc(100001); printf("%d\n", fib(100000, &t[0])); free(t); return 0; }
Tekintsük az alábbi C programot (~/szimp2/fibt2.c):
#include <stdio.h> int fib(int n, int *t) { int f; if (n<2) f=n; else f=fib(n-1, t)+fib(n-2, t); t[n]=f; return f; } int main(void) { int t[2]; printf("Hello, World! %d\n", fib(22, t)); return 0; }
Kipróbálás:
$ gcc -s -O2 -W -Wall -o fibt2 fibt2.c $ ./fibt2 17711 Szegmens hiba
Kiírja a helyes eredményt (17711), majd szegmens hiba lesz. Azért, mert a t tömb csak 2 elemű, és 22 elemet rak bele a fib. Az extra elemek felülírják a main() visszatérési címét.
Indulás után egy kicsivel így néz ki a verem:
fib(2) visszatérési címe n: 2 --------------------------- fib(3) visszatérési címe n: 3 --------------------------- ... --------------------------- fib(22) visszatérési címe n: 22 --------------------------- t[0]==0 t[1]==1 main() visszatérési címe ---------------------------
Mikor a fib-ek visszatérnek, így néz ki a verem:
t[0]==0 t[1]==1 t[2]==1 (a main() visszatérési címe felülírva) t[3]==2 ... t[22]==17711
Ezután a main() már nem tud visszatérni, mert a visszatérési címét felülírták.
Próbáljuk valgrinddel:
$ gcc -g -W -Wall -o fibt2g fibt2.c $ ./valgrind ./fibt2g ... Hello, World! 17711 ==29070== Jump to the invalid address stated on the next line ==29070== at 0xD00000008: ??? ==29070== Address 0xD00000008 is not stack'd, malloc'd or (recently) free'd ==29070== ==29070== Process terminating with default action of signal 11 (SIGSEGV) ==29070== Bad permissions for mapped region at address 0xD00000008 ==29070== at 0xD00000008: ??? ...
A valgrind se mondja meg, hol a hiba (stack trace), mert a visszatérési cím alapján jelezne.
14. előadás (2007-05-18)
Mivel a március 15. előtti szombaton plusz egy előadást tartottunk, ezért a félévben egy előadást ki kell hagyni. Ez lesz az. Pontosabban: az előadás meg lesz tartva, és továbbmegyünk az anyagban, de nem kötelező eljönni. Ennek az előadásnak az anyaga nem lesz számonkérve (nem is lehet, mivel a ZH-t az előző héten már megírtuk). Az előadás előtti gyakorlat természetesen kötelező.
A második ZH
Időpontok:
- elmaradt keddi gyakorlatok pótlása: 2006. május 8. kedd, 12:00--14:15.
- konzultáció: 2007. május 10. csütörtök, 14:14--19:19. H.27. A konzultáció korábban is véget érhet a meghirdetettnél: ha 15 percen belül nem hangzik el kérdés.
- ZH papíron: 2007. május 11. péntek, 12:00--14:00. K.mf.65. Felügyel: Sisak Áron. Javítja: Sisak Áron.
- ZH programozás (opcionális): 2007. május 11. péntek, 14:00--16:00, H.57. Felügyel: Sisak Áron.
- A pótZH 7 nappal a ZH után lesz, a napon belül azonos időbeosztás szerint. Terem: J.208.
- A gyakIV 14 nappal a ZH után lesz, lásd a saját fejezetében.
A ZH témája objektum-orientált programozás Ruby nyelven. A ZH-n 1 db, kézzel, kék tollal írt A4-es lapnyi puskát lehet használni. A ZH-n kb. 50% programozási és 50% elméleti feladat lesz. Aki a programozási feladatokat számítógéppel szeretné írni, az jelentkezzen e-mailben a tárgy előadójánál. A számítógépes megoldás lehetőségét technikai okokból csak abban az esetben tudjuk biztosítani, ha max. 10 jelentkező lesz.
Mely beépített metódusokat kell ismerni
A tanult Ruby beépített osztályok és metódusok:
- Object
- class
- methods
- Float
- abs + - * < <= == != >= > <=> **
- Fixnum
- abs + - * < <= == != >= > <=> << >> **
- Bignum
- (semmit)
- Integer
- (semmit)
- String
- + =~ !~ split split! chomp chomp! reverse $1 $2 <=> upcase upcase! downcase downcase! reverse reverse!
- Array
- [] []= + - | & join each size << push max min find_all sort sort! sort_by sort_by! include?
- Module
- (semmit)
- Class
- superclass
- TrueClass
- && || ! == !=
- FalseClass
- && || ! == !=
- NilClass
- == !=
- Hash
- [] []= has_key? keys values size delete each_pair
- Regexp
- (semmit)
- Math
- sqrt PI
Van még: STDIN.each_line, p, print, puts.
Hogy a fentiek mit csinálnak, annak itt lehet utánanézni: http://www.ruby-doc.org/core/
Mintafeladatok
Az alábbiakhoz hasonló és másféle feladatok is várhatók a második ZH-n.
Elméleti feladatok:
- Az
x.foo()
hívás esetén mely osztályokban és milyen sorrendben keresi a Ruby a meghívandó metódust? - Hozzon létre egy olyan x objektumot, melyre
x.kind_of?(Alma) && x.class!=Alma
- Mikor áll a C-beli
int i=1; while (i!=0) i*=2;
ciklus? Mikor áll le a Ruby-belii=1; while i!=0; i*=2; end
ciklus? - Írjon egy olyan osztályt Ruby nyelven, amely paraméteres konstruktorral rendelkezik, és hozzon létre két példányt. Hányszor hívódik meg a konstruktor?
- Mutasson példát arra, hogy az
x.leszed()
hívás nem a Gyumolcs osztályon belül definiált leszed metódust hívja, nohax.kind_of?(Gyumolcs)
- Mit ad vissza a
self
egy metóduson belül? - Mi a különbség egy metóduson belül a
foo=5
,@foo=5
és aself.foo=5
értékadások között? - Írjon egy olyan metódust, melyben a
p foo
hívás kétszer szerepel, de csak az egyik esetben írja ki a foo nevű lokális változó értékét, a másik esetben mást ír ki. - Írja át az alábbi kódot úgy, hogy
@a<=@b
mindig teljesüljön. Ha a hívó olyan helyzetet akarna előidézni, amely megsérti a fenti feltételt, akkor dobjon kivételtfail
-lel. (Segítség: 3 db metódusba kellfail
-t tenni.) mintamegoldás
class Intervallum attr_accessor :a, :b def initialize(a, b) @a=a; @b=b end end
- Az alábbi kód végrehajtása után a a Ruby lefuttatja a szemétgyűjtést. Rajzolja fel az
# itt
sor végrehajtásakor az objektumokat, és azt, hogy az a és b változók mely objektumra mutatnak. Mely objektumok maradnak meg, és melyeket szabadítja fel a szemétgyűjtés?
class Gyerek attr_accessor :nev, :kov def initialize(nev) @nev=nev; @kov=nil; end end def egyik() pal=Gyerek.new("Pal") kata=Gyerek.new("Kata") peter=Gyerek.new("Peter") pal.kov=kata; kata.kov=peter; peter.kov=pal end def masik() sandor=Gyerek.new("Sandor") jozsef=Gyerek.new("Jozsef") benedek=Gyerek.new("Benedek") sandor.kov=jozsef; jozsef.kov=benedek end a=egyik b=masik # itt a=nil
Programozási feladatok:
- Írjon Ruby-programot sorhosszcsokkeno.rb néven, amely beolvassa a bemenet sorait, majd a sorhossz szerinti csökkenő sorrendben kiírja a sorokat. Az egyenlő hosszúságú sorok sorrendje tetszőleges.
- Írjon Ruby-programot abszoluterteknovekvo.rb néven, amely beolvassa a bemenet sorait (a sorok egész számokat tartalmaznak), és kiírja a sorokat az abszolút értékek növekvő sorrendjében. Az egyenlő abszolútértékű számok sorrendje tetszőleges.
- Írjon Ruby-programot palindrom.rb néven, amely beolvassa a bemenet sorait, és kiírja a palindrom sorokat. Egy string palindrom, ha a megfordítása önmaga. Segítség: beolvasás után, de még összehasonlítás előtt az
s.chomp!
hívással törölje a soremelést a string végéről. - Írjon Ruby-programot palindromlehet.rb néven, amely beolvassa a bemenet sorait, és kiírja azokat a sorokat, melyekben a betűk átvarálásával palindrom string készíthető. Egy string palindrom, ha a megfordítása önmaga. 1. segítség: beolvasás után, de még összehasonlítás előtt az
s.chomp!
hívással törölje a soremelést a string végéről. 2. segítség: rendezze a string karaktereit azs=s.split().sort.join
hívással. - Írjon Ruby-programot rendszamtobbszor.rb néven, amely beolvassa a bemenet sorait, és kiírja azokat a rendszámokat, melyek 2-szer vagy többször szerepelnek. A rendszám három betűből, egy kötőjelből és három számjegyből áll. Segítség: egy sort szavakra lehet bontani a
s.split
hívással, ami stringekből álló tömböt ad vissza. - Írjon Ruby-programot egyszer.rb néven, amely beolvassa a bemenet sorait, és csak azokat a sorokat írja ki (tetszőleges sorrendben), melyek pontosan egyszer szerepelnek.
- Melyik az a legkisebb kettőhatvány, ami már nem Fixnum? Írjon programot (marnemfixnum.rb néven), ami megadja a választ.
- Adott két objektum: a és b. Írjon Ruby-függvényt, amely visszaadja azt a legmélyebb osztályt, amely mindkét objektumnak szülőosztálya. Deklaráció:
def kozosos(a,b)
- Készítsen egy Teglalap nevű osztályt Ruby nyelven a teglalap.rb fájlba, amely egy tengélypárhuzamos téglalapot tartalmaz. Létrehozására példa:
t=Tegalalap.new(3,4,1,2)
, ahol a bal alsó sarok a (3,4), a szélessége 1, a magassága 2, tehát a jobb felső sarok a (4,6).- Tegye lehetővé a szélső koordináták lekérdezését, például
p t.llx, t.lly
a bal alsó sarkotp t.urx, t.ury
jobb felső sarkot írja ki. - Tegye lehetővé a téglalap méretének lekérdezését, például
p t.width, t.height
a szélességet, majd a magasságot írja ki. - Tegye lehetővé a téglalap méretének megváltoztatását, például
p.width=42
a téglalap szélességét, ap.height=137
pedig a téglalap magasságát változtatja meg. A változtatás a bal alsó sarkot helybenhagyja, a többi csúcsot pedig értelemszerűen mozgatja. - Tegye lehetővé az egyes csúcspontok lekérdezését:
t.ul
a bal felső,t.ur
a jobb felső,t.lr
a jobb alsó,t.ll
a bal alsó sarkot adja vissza. Egy csúcspont egy kételemű tömb:[x,y]
. - Tegye lehetővé a téglalap eltolását: a
t.eltol!(dx,dy)
hívás dx-et hozzáad minden pont x koordintájához, és dy-ot adjon hozzá minden pont y koordinátájához, at.eltol(dx,dy)
hívás pedig egy új téglalapot adjon vissza, amely t-hez képest (dx,dy)-nal van eltolva. - Tegye lehetővé a súlypont lekérdezését: a
t.sulypont
hívás adja vissza a súlypontot[cx,cy]
tömbként, at.cx
hívás adja vissza a súlypont x koordinátáját, at.cy
hívás pedig adja vissza a súlypont y koordinátáját. - Tegye lehetővé a téglalap eltolását a súlypont mozgatásával. A
t.cx=42
hívás x irányban, at.cy=137
hívás pedig irányban mozgassa a téglalapot úgy, hogy a súlypont a megadott koordinátára kerüljön. - Tegye lehetővé a téglalap nagyítását: a
t.nagyit!(m)
hívás a tömegközéppontból m-szeresére nagyítsa a téglalapot, at.nagyit(m)
hívás pedig egy új téglalapot adjon vissza, amely az eredetihez képest a fenti módon van kinagyítva. - A téglalap méretét megváltoztató
t.width=()
ést.height=()
metódusokat módosítsa úgy, hogy negatív méretet ne lehessen megadni. Definiáljon egy kivételosztályt NegativMeretHiba néven, és dobja ezt a kivételt, ha a hívó negatív méretet akar beállítani. - A téglalap létrehozásakor meghívott függvényt módosítsa úgy, hogy negatív méretű téglalapot ne lehessen megadni. Dobjon NegativMeretHiba kivételt, ha a hívó negatív méretű téglalapot akar létrehozni.
- Tegye lehetővé a téglalap szélső koordinátáinak megváltoztatását. A megvalósítandó hívások:
t.llx=1; t.lly=2; t.urx=3; t.ury=4
. Ha a hívó olyan értéket akar beállítani, hogy a téglalap mérete negatív lenne, dobjon NegativMeretHiba kivételt. - Írjon függvényt sulypontok néven, amely soronként beolvassa a bemenetet, minden sorban 4 számot várjon (elemekre bontás:
s.split
hívással), ezek a számok a bal alsó sarok x majd y koordinátája, a szélesség és a magasság. A kimenetre a téglalap súlypontját írja (soremeléssel lezárva), a sulypont metódus hívásával. A NegativMeretHiba kivételt kapja el, ekkor a kimenetre egy N betű és egy soremelés kerüljön.
- Tegye lehetővé a szélső koordináták lekérdezését, például
- Készítsen Szamegyenes néven osztályt Rubyban. A számegyenes olyan, mint egy tömb, de nem csak pozitív, hanem negatív irányban is tetszőlegesen bővíthető. Az adatok tárolásához használjon 2 tömböt: @p és @n. Például a számegyenes 5-ödik elemét @p[5]-ben, a -5-ödik elemét pedig @n[4]-ben tárolja. Kezdetben mindkét tömb legyen üres.
- Írja meg a
def [](i); ... end
metódus törzsét, mely visszaadja a számegyenes i-edik elemét. - Írja meg a
def []=(i,uj); ... end
metódus törzsét, mely a számegyenes i-edik elemét megváltoztatja uj-ra, és visszaadja uj-t. - Írja meg a
def size(); ... end
metódus törzsét, amely visszaadja a számegyenes elemeinek összszámát, vagyis a @p és @n tömbök összméretét. - Írja meg a
def tukroz!(); ... end
metódus törzsét, amely a számegyest tükrözi az origóra, vagyis minden i-re az i-edik elem helyet cserél a -i-edik elemmel.
- Írja meg a
- Készítsen KorlatosTomb néven osztályt Rubyban. A KorlatosTomb egy olyan tömb, amelynek kezdeti mérete később nem változtatható meg. Megvalósításához a @t Ruby tömböt (Array) használja.
- Írja meg a konstruktort, amely paraméterben várja azt az m méretet, melyre korlátozva van a tömb.
- Írja meg a paraméter nélküli size metódust, amely visszaadja a konstruktornak átadott m méretet.
- Írja meg a
def [](i); ... end
metódus törzsét, mely visszaadja a korlátos tömb i-edik elemét. Ha az i kívül esik az intervallumon, kivételt dob (fail
). - Írja meg a
def []=(i,uj); ... end
metódus törzsét, mely a korlátos tömb i-edik elemét megváltoztatja uj-ra, és visszaadja uj-t. Ha az i kívül esik az intervallumon, kivételt dob (fail
).
Mintafeladatok megoldásai
- Intervallum
class Intervallum attr_reader :a, :b def initialize(a, b) fail unless a <= b @a=a; @b=b end def a=(a) fail unless a <= @b @a=a end def b=(b) fail unless @a <= b @b=b end end
- Háromszög és szemétgyűjtés
- A 2. ZH 1. feladatának kicsit módosított változata:
- nincs attr_accessor, mert felesleges,
- #itt és #ott és #amott ki is írjuk, mi van az objektumokban.
- A 2. ZH 1. feladatának kicsit módosított változata:
class Haromszog def initialize(a, b, c) fail unless a+b>c and a+c>b and b+c>a @a=a; @b=b; @c=c end end class SzabalyosHaromszog < Haromszog def initialize(a) super(a, a, a) end end
begin h = Haromszog.new(3,4,5); # haromszog1 sz = SzabalyosHaromszog.new(6); # szabalyosharomszog1 h2 = SzabalyosHaromszog.new(5); # szabalyosharomszog2 # itt p '# itt h: ' + h.to_s + ' sz: ' + sz.to_s h = Haromszog.new(1,2,3); # haromszog2 rescue h2 = nil # ott p '# ott h: ' + h.to_s + ', sz: ' + sz.to_s sz = h ensure h = sz # amott p '# amott h: ' + h.to_s + ', sz: ' + sz.to_s end
- Készítünk 3 objektumot, #itt nem szabadítható fel semmi, h haromszog1 és sz szabalyosharomszog1.
- A haromszog2 létre sem jön (háromszög-egyenlőtlenség miatt)
- így h marad a korábbi, hiszen az értékadás csak a konstruktorhívás után kerül(ne) sorra,
- és átkerülünk a rescue blokkba.
- A rescue blokkban először h2 változik, emiatt #ott h marad haromszog1 és sz marad szabalyosharomszog1, ellenben szabalyosharomszog2 felszabadítható,
- később sz-be is haromszog1 kerül.
- Az ensure blokk mindig lefut,
- h-ba sz, azaz haromszog1 kerül (nem mintha eddig nem az lett volna),
- végül #amott felszabadítható szabalyosharomszog1 is.
GyakIV
A gyakIV 2007. május 25-én, pénteken 12:00-tól 16:00-ig kerül megrendezésre a K.A.62-ben. A gyakIV a teljes féléves anyagból az, ami a wikiben meg van említve.