The Object-Oriented Programming Language RubyDe Nederlandse Ruby Homepage




9. Standaard libraries



9.1 Wat geeft instance_methods(nil) terug?

De methode instance_methods geeft een array met de namen van de methodes waarop de receiver reageert terug . Het array bevat ook methodes uit super klassen en mixin modules.

instance_methods(nil) geeft uitsluitend de namen van de methodes die in de klasse van het object zijn gedefinieerd.

9.2 Hoe werken random number seeds?

Dat hangt ervan af. In Ruby versies voor 1.5.2, had de random number generator (default) een vast seed, en zou zodoende iedere keer dat het programma gedraaid werd dezelfde serie getallen produceren. Als je iest minder voorspelbaar wilde zijn riep je srand aan om een ander seed op te geven.

Latere Ruby versies werken anders. Als rand word aangeroepen zonder dat daarvoor een aanroep van srand is gedaan, zal Ruby een (min of meer) willekeurig seed genereren. Opeenvolgende sessies van een programma dat srand niet gebruikt leidt nu tot verschillende nummer reeksen. Om het vroegere voorspelbare gedrag terug te krijgen (bijvoorbeeld om te testen) roep je srand aan met een vast seed.

9.3 Wat is het verschil tussen een directe waarde en een referentie?

Fixnum, true, nil, en false zijn geimplementeerd als directe waardes. Bij directe waardes bevatten de variabelen de objecten zelf in plaats van referenties naar die objecten.

Singleton methodes kunnen voor dit soort objecten niet gedefinieerd worden. Twee Fixnums met de zelfde waarde verwijzen altijd naar dezelfde instantie van een object. Dus instance variabelen voor de Fixnum met de waarde "een" worden gedeeld met alle andere "enen" in het systeem. Daarom kan er geen sprake zijn van een singleton methode voor een bepaalde instantie.

9.4 Wat is het verschil tussen nil and false?

Eerst de overeenkomst. nil en false zijn de enige twee waarden die als onwaar gelden in een booleaanse context.

Echter, het zijn instanties van verschillende klassen (NilClass en FalseClass), en ze vertonen in een niet-booleaanse context ander gedrag.

Wij raden aann dat predicaat methodes (dat zijn die wiens naam eindigd met een vraagteken) true or false teruggeven. Andere methodes die het falen van een bewerking moeten signaleren zouden nil moeten teruggeven.

9.5 Ik lees een bestand in en verander het, maar het bestand op schijf is niet veranderd!

   
open("example", "r+").readlines.each_with_index{|l, i|
l[0,0] = (i+1).to_s + ": " }

Dit programma voegt geen regelnummers toe aan het bestand "example". Het leest de inhoud van het bestand en zet het regelnummer voor iedere regel, maar de gegevens worden nooit teruggeschreven naar schijf. De code hierna veranderd het bestand op schijf wel (op een wat gevaarlijke manier omdat er geen backup gemaakt word voor de update start):

   
io = open("example", "r+")
ary = io.readlines
ary.each_with_index{|l, i| l[0,0] = (i+1).to_s + ": "}
io.rewind
io.print ary
io.close

9.6 Hoe kan ik een bestand verwerken en de inhoud bijwerken?

Gebruik de command-line optie -i, of de ingebouwde variabele $-i, daarmee leest je een bestand in en overschrijf je het.

De code van de vorige vraag, die regelnummers toevoegt aan een bestand, kan waarschijnlijk het best worden geschreven met de volgende techniek:

   
$ ruby -i -ne 'print "#$.: #$_"' example

Als je het oorspronkelijke bestand wil bewaren kan je -i.bak gebruiken om een backup te maken.

9.7 Ik heb een bestand weggeschreven en gekopieerd maar het einde van de kopie is weg!

Deze code werkt niet goed:

   
open('file', 'w').print "This is a file.\n"
system 'cp file newfile'

Omdat de I/O gebufferd is, wordt het bestand gekopieerd voordat de inhoud naar de schijf is weggeschreven. newfile zal waarschijnlijk leeg zijn. Als het programma stopt worden de buffers geleegd en zal file de verwachte inhoud hebben.

Het probleem doet zich niet voor als je file sluit voor je het kopieerd.

   
f = open('file', 'w')
f.print "This is a file.\n"
f.close
system "cp file newfile"

9.8 Hoe vind ik het regelnummer van het actieve invoer bestand?

Als je uit een bestand leest houd Ruby het regelnummer bij in de globale variabele $.. Deze is ook beschikbaar via het lineno attribuut van het File object.

De speciale constante ARGF is een bestand-achtig object dat gebruikt kan worden om alle invoer bestanden die gespecificeerd zijn op de command-line te lezen (of de standaard input zls er geen bestanden zijn opgegeven). ARGF word implicietm gebruikt in code zoals deze::

   
while gets
print $_
end

In dit geval bevat  $. het cummulatieve aantal gelezen regels van alle invoer bestanden. Om het regelnummer van het actieve bestand te achterhalen gebruik je:

   
ARGF.file.lineno

De naam van het actieve bestand vind je door middel van ARGF.file.path.

9.9 Hoe kan ik less gebruiken om de uitvoer van mijn programma te tonen?

Ik heb dit geprobeerd maar dat werkte niet:

   
f = open '|less', 'w'
f.print "abc\n"

Dat komt omdat het programma direct stopt en less dus nooit de kans krijgt om de output te zien. Gebruik close om te wachten tot less klaar is.

   
f = open '|less', 'w'
f.print "abc\n"
f.close

9.10 Wat gebeurd er met een File object waar niet langer naar word verwezen?

A File object waar niet langer naar wordt verwezen is komt in aanmerking voor garbage collection. Het bestand word automatisch gesloten als het object door de  garbadge collector word opgeruimd.

9.11 Ik voel me ongemakkelijk als ik een bestand niet sluit.

Er zijn minstens vier goede manieren om ervoor te zorgen dat je een bestand sluit:

   
(1)  f = open "file"
begin
f.each {|l| print l}
ensure
f.close
end

(2) File.open("file") { |f|
f.readlines.each { |l| print l }
}

(3) IO.foreach("file") {|l| print l}

(4) IO.readlines("file").each {|l| print l}

9.12 Hoe sorteer ik bestanden op hun tijdstip van laatste wijziging?

   
Dir.glob("*").sort{|a,b| File.mtime(b) <=> File.mtime(a)}

Dit werkt (het geeft een lijst terug in omgekeerde chronologische volgorde) maar is niet erg efficient omdat het de wijzigingstijden bij iedere vergelijking opvraagt bij het operating system.

Met wat meer complexiteit kunnen we efficienter te werk gaan:

   
Dir.glob("*").collect! {|f| [File.mtime(f), f]}.
sort{|a,b| b[0] <=> a[0]}.collect! {|e| e[1]}

9.13 Hoe tel ik het aantal keren dat een woord voorkomt in een bestand?

   
freq = Hash.new(0)
open("example").read.scan(/\w+/){|w| freq[w] += 1}
freq.keys.sort.each {|k| print k, "-", freq[k], "\n"}

Produces:

and-1
is-3
line-3
one-1
this-3
three-1
two-1

9.14 Waarom is een lege string niet gelijk aan false?

Q: Een lege string ("") geeft true terug in een conditionele expressie! In Perl is dat false.

A: In Ruby zijn alleen nil en false onwaar in een conditionele context. Op deze manier word aan snelheid gewonnen -- nil en false hebben directe waarden zodat ze kunnen worden getest zonder een referentie te hoeven volgen.

Je kan empty? gebruiken, de string met "" vergelijken of de lengte van de string met 0 vergelijken om er achter te komen of een string leeg is.

9.15 Hoe sorteer ik strings op alfabetische volgorde?

Als je strings wil sorteren als 'AAA', 'BBB', ..., 'ZZZ', 'aaa', 'bbb' dan voldoet de ingebouwde vergelijkingsfunctionaliteit.

Als je geen rekening wil houden met het verschil tussen hoofd- en kleine letters moet je in het sorteerblock de strings in kleine letters vergelijken:

   
array = %w( z bB Bb BB bb Aa aA AA aa a )
puts array.sort { |a,b| a.downcase <=> b.downcase }

Produces:

a
aa
AA
aA
Aa
bb
BB
bB
Bb
z

Als je zodanig wil sorteren dat de 'A's en de 'a's bij elkaar staan maar dat 'a' een hogere waarde heeft dan 'A' (dus 'Aa' komt na 'AA' maar voor 'AB'), moet je dit gebruiken:

   
puts array.sort { |a,b|
(a.downcase <=> b.downcase).nonzero? || a <=> b
}

Produces:

a
AA
Aa
aA
aa
BB
Bb
bB
bb
z

9.16 Wat geeft "abcd"[0] terug?

Dit geeft de character code voor ``a'' terug: 97(Fixnum).Je kan de character code weergeven als een integer constante door het tken te prefixen met een vraagteken dus ?a is ook 97(Fixnum).

9.17 Hoe zet ik tabs om naar spaties?

Als a de string bevat die moet worden omgezet zou je een van de volgende mogelijkheden kunnen gebruiken:

   
  1 while a.sub!(/(^[^\t]*)\t(\t*)/){$1+' '*(8-$1.size%8+8*$2.size)}
#or
1 while a.sub!(/\t(\t*)/){' '*(8-$~.begin(0)%8+8*$1.size)}
#or
a.gsub!(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")}

9.18 Hoe kan ik een backslash in een reguliere expressie opnemen?

Regexp.quote('\\') escaped een backslash.

Het wordt moeilijker als je sub en gsub gebruikt. Stel dat je gsub(/\\/, '\\\\') schrijft om iedere backslash door twee backslashes te vervangen. Het tweede argument word tijdens de syntax analyse omgezet in '\\'. Als de substitutie plaatsvind zet de reguliere expressie engine dit om naar '\', dus iedere backslash wordt vervangen door een andere en er gebeurd du niets. Je had gsub(/\\/, '\\\\\\') moeten gebruiken!

Omdat \& de gematchte string bevat kan je ook gsub(/\\/, '\&\&') schrijven.

Als je de block vorm van gsub gebruikt, gsub(/\\/){'\\\\'}, wordt de te substitueren string maar een keer (tijdens de syntax analyse) geanalyseerd en is het resultaat dat wat je bedoelde.

9.19 Wat is het verschil tussen sub and sub!?

In sub, word een kopie van de receiver gegenereerd, gesubstitueerd en teruggegeven.

In sub!, wordt de receiver gewijzigd en teruggegeven als er een match word gevondenthe receiver is altered and returned if any match was found. Word er geen match gevonden dan word nil teruggegeven.

Methodes zoals sub! worden destructieve methodes genoemd die de attributen van de receiver wijzigen. Als er twee methodes zijn waarvan er een destructief is eindigd de naam van de destructieve methode met de suffix !.

   
def foo(str)
str = str.sub(/foo/, "baz")
end

obj = "foo"
foo(obj) # -> "baz"
obj # -> "foo"

def foo(str)
str = str.sub!(/foo/, "baz")
end

foo(obj) # -> "baz"
obj # -> "baz"

9.20 Waar matcht \Z?

\Z matcht precies voor de laatste \n als de string eindigd met een \n, anders matcht het 't einde van de string.

9.21 Wat is het verschil tussen ".." en "..."?

.. neemt het rechtse argument mee in de range, ... doet dat niet.

9.22 Heeft Ruby functie pointers?

Naar een Proc object gegenereed door Proc.new, proc, of lambda kan verwezen worden door een variabele zodat je zou kunnen zeggen dat die variabele een functiepointer is. je kan ook refereren aan de methodes van een bepaald object door de Object.method notatie te gebruiken.

9.23 Wat is het verschil tussen thread and fork?

Ruby threads zijn geimplementeerd binnen de interpreter tewijl fork het operating system gebruikt om een apart subprocess te starten.

Thread and fork hebben de volgende eigenschappen:

  • fork is langzaam, thread is dat niet.
  • fork deelt zijn geheugenruimte niet.
  • thread veroorzaakt geen "thrashing".
  • thread werkt onder DOS.
  • wanneer thread in een deadlock raakt wordt het hele proces gestopt.
  • fork kan gebruikmaken van pauzes die veroorzaakt worden door het wachten op I/O, thread doet dat niet (althans niet zonder wat aansporing).

Het is niet verstandig om fork en thread door elkaar te gebruiken.

9.24 Hoe gebruik ik Marshal?

Marshal word gebruikt om een object op te slaan in een bestand of een string om deze op en later tijdstip weer op te bouwen. Objecte kunnen worden opgeslagen door:

   
Marshal.dump obj [, io ] [, lev]

io is een scrijfbaar IO object, lev bepaald het niveau tot waarop aan objecten word gerefereerd om te worden opgeslagen. Als er  lev niveau's objecten worden gerefereerd en er bestaan nog steeds object referenties dan slaat  dump alleen de referentie en niet het object waarnaar gerefereerd word op. Dit moet je niet willen omdat de objecten waarnaar gerefereerd word later niet meer kunnen worden opgebouwd.

Als io word wegggelaten worden de ge-marshalde objecten teruggegeven als string.

Objecten laadt je door:

   
   obj = Marshal.load io
#or
obj = Marshal.load str

Hier is io een leesbaar IO object, str is de weggeschreven string.

9.25 Heeft Ruby exception handling?

Ruby heeft een flexibele exception handling mogelijkheden:

   
begin
statements which may raise exceptions.
rescue [exception class names]
statements when an exception occurred.
rescue [exception class names]
statements when an exception occurred.
ensure
statements that will always run
end

als er een exception plaats vind in de begin clause, word de rescue clause met de bijbehorende exception naam uitgevoerd. De ensure clause word altijd uitgevoerd ongeacht of er een exception optreed. De rescue and ensure clauses mogen worden weggelaten.

Word er geen exception class opgegeven voor de rescue clause, dan geld ze voor de StandardError exception en voor exceptions die een is_a? relatie met StandardError hebben.

Deze expressie geeft de waarde van de begin clause terug.

De laatst opgetreden exception word opgeslagen in de globale variabele $! (dus het type ervan kan worden opgevraagd door $!.type).

9.26 Hoe gebruik ik trap?

trap associeerd code blocks with externe events (signals).

   
trap("PIPE") {raise "SIGPIPE"}