Programmering i Ruby

Den Pragmatiske Programmerers Veiledning

Forrige < Innhold ^
Neste >

Når problemer oppstår



Dessverre så er det mulig å skrive program med feil i Ruby. Vi er lei for det.

Men bekymre deg ikke! Ruby har flere fasiliteter som vil hjelpe deg å debugge dine programmer. Vi vil se på disse fasilitetene, og så vil vi se på noen vanlige feil som gjøres i Ruby, og hvordan man fikser dem.

Ruby Debugger

Ruby kommer med en degugger som er bygd inn i base-systemet. Du kan kjøre debuggeren ved å sette igang fortolkeren med -r debug-opsjonen, og sammen med hvilke som helst andre Ruby-opsjoner og navnet på ditt script:

ruby -r debug [
            opsjoner
            ] [
            programfil
            ] [
            argumenter
            ]

Debuggeren støtter det vanlige utvalget av egenskaper du kunne forvente, inkludert muligheten til å sette brekkpunkter, å gå inni og steppe over metodekall, og å vise stakk-rammer og variabler.

Den kan også liste instansmetodene definert for et spesielt objekt eller klasse, og tillater deg å liste og kontrollere separate tråder innen Ruby. Tabell 12.1 på side 131 viser alle kommandoene som er tilgjengelige i debuggeren.

Hvis din Ruby har readline-støtte på, kan du bruke markør-tastene til å bevege deg fram og tilbake i kommando-historien og bruke linje-editerings-kommandoer til å korrigere tidligere input.

For å gi deg en ide på hvordan Ruby-debuggeren er, så vises det en eksempelsesjon her.

% 
              ruby -rdebug t.rb
            
Debug.rb
Emacs support available.
t.rb:1:def fact(n)
(rdb:1) 
              list 1-9
            
[1, 10] in t.rb
=> 1  def fact(n)
   2    if n <= 0
   3      1
   4    else
   5      n * fact(n-1)
   6    end
   7  end
   8
   9  p fact(5)
(rdb:1) 
              b 2
            
Set breakpoint 1 at t.rb:2
(rdb:1) 
              c
            
breakpoint 1, fact at t.rb:2
t.rb:2:  if n <= 0
(rdb:1) 
              disp n
            
  1: n = 5
(rdb:1) 
              del 1
            
(rdb:1) 
              watch n==1
            
Set watchpoint 2
(rdb:1) 
              c
            
watchpoint 2, fact at t.rb:fact
t.rb:1:def fact(n)
1: n = 1
(rdb:1) 
              where
            
--> #1  t.rb:1:in `fact'
    #2  t.rb:5:in `fact'
    #3  t.rb:5:in `fact'
    #4  t.rb:5:in `fact'
    #5  t.rb:5:in `fact'
    #6  t.rb:9
(rdb:1) 
              del 2
            
(rdb:1) 
              c
            
120

Interaktiv Ruby

Hvis du vil leke med Ruby, er det en fasilitet kalt Interaktiv Ruby---irb(forkortet). irb er essensielt et Ruby-"skall", lignende i konsept på et operativ-system-skall (komplett med jobb-kontroll). Den tilbyr et miljø hvor du kan "leke rundt" med språket i nåtid. Du setter igang irb på kommando-linjen.

irb [
            irb-options
            ] [
            ruby_script
            ] [
            opsjoner
            ]

irb vil vise verdien på hvert uttrykk ettersom du gjør det ferdig. For eksempel:

% irb
irb(main):001:0> 
              a = 1 +
            
irb(main):002:0* 
              2 * 3 /
            
irb(main):003:0* 
              4 % 5
            
2
irb(main):004:0> 
              2+2
            
4
irb(main):005:0> 
              def test
            
irb(main):006:1> 
              puts "Hello, world!"
            
irb(main):007:1> 
              end
            
nil
irb(main):008:0> 
              test
            
Hello, world!
nil
irb(main):009:0> 

irb tillater deg også å lage subsesjoner, hver av disse kan ha sin egen kontekst. For eksemepel kan du lage en subsesjon med den samme (topp-nivå) konteksten som original-sesjonen, eller skape en subsesjon i konteksten til en spesiell klasse eller instans. Eksempelsesjonen vist i figur 12.1 på side 126 er noe lengre, men viser deg hvordan du kan skape subsesjoner og svitsje mellom dem.

Figur 12.1: Eksempel på sesjon i irb

For en full beskrivelse av alle kommandoene som irb støtter, se referansen som begynner på side 517.

Som med debuggeren, hvis din versjon av Ruby ble bygd med GNU Readline-støtte, kan du benytte pil-tastene (som i Emacs) eller vi-type taste-bindinger for å editere individuelle linjer, eller gå tilbake og re-eksekvere eller editere en tidligere linje--- akkurat som et kommando-skall.

irb er et meget bra læreverktøy: det er veldig hendig hvis du vil prøve ut en ide raskt og se om ideen virker.

Editor-støtte

Ruby er designet for å lese et program i en sveip, dette betyr at du kan pipe et helt program til Rubys standard input og det vil virke helt fint.

Vi kan dra fordel av denne egenskapen for å kjøre Ruby-kode fra innsiden av en editor. I Emacs, for eksempel, kan du velge et område av Ruby-tekst og bruke kommandoen Meta-| for å kjøre Ruby. Ruby-fortolkeren vil bruke det valge området som standard input og output vil gå til et buffer kalt "*Shell Command Output*"." Denne egenskapen har vært nokså nyttig for oss når vi skrev denne boken---bare velg et par linjer av Ruby midt i et avsnitt og prøv det ut!

Du kan gjøre noe av det samme i vi-editoren ved å bruke ":!Ruby som erstatter programteksten med sin output, eller ``:w[visible space]!ruby'', som viser outputen uten å påvirke bufferet. Andre editorer har lignende egenskaper.

Mens vi er på temaet, dette ville være et bra sted å nevne at det er en egen Ruby-modus for Emacs som er inkludert i distribusjonen som misc/ruby-mode.el. Det er også en del syntaks-framhevende moduler for vim(en forbedret versjon av vi-editoren), jed, og andre editorer tilgjengelige på nettet. Sjekk Ruby-FAQ'en for nåværende lokasjoner og tilgjengelighet.

Men det virker ikke!

Så du har lest igjennom nok av boken, du starter å skrive ditt helt egne Ruby-program, og det virker ikke. Her er en liste av vanlige feller og noen tips.

Det er en hovedteknikk som gjør det å skrive Ruby-kode både enklere og mer morsomt. Utvikle dine applikasjoner inkrementelt. Skriv noen få linjer med kode, og så kjør dem. Skriv noen få til og kjør disse. En av de store fordelene med et utypet språk er at ting ikke trenger å være komplette før du bruker dem.

Men det er for sakte!

Ruby er et fortolket, høynivå språk, og som sådan er ytelsen ikke så bra som for et lavnivå-språk som C. I denne seksjonen vil vi liste noen grunnleggende ting du kan gjøre for å forbedre ytelsen. Se også i indekset under Ytelse for andre pekere.

Lage lokale variabler utenfor blokker

Prøv å definere variabler brukt i en blokk før blokken kjøres. Når du itererer over et veldig stort sett av variabler, kan du forbedre kjørehastigheten noe ved å deklarere alle iteratorvariabler på forhåd. I dette første eksemplet nedenfor, må Ruby lage nye x og y-variabler på hver iterasjon, men i den andre versjonen trenger den ikke det. Vi bruker benchmark-pakken fra Ruby Application Archive for å sammenligne løkkene:

require "benchmark"
include Benchmark
n = 1000000
bm(12) do |test|
  test.report("normal:")    do
    n.times do |x|
      y = x + 1
    end
  end
  test.report("predefine:") do
    x = y = 0
    n.times do |x|
      y = x + 1
    end
  end
end
produserer:
                  user     system      total        real
normal:       2.490000   0.000000   2.490000 (  2.462660)
predefine:    2.190000   0.010000   2.200000 (  2.196071)

Bruk Profileren

Ruby kommer med en kode-profiler (dokumentasjon begynner på side 454). I seg selv er ikke det så overraskende, men når det går opp for deg at profileren er skrevet i ca. 50 linjer Ruby, gjør det Ruby til et ganske imponerende språk.

Du kan legge til profiling i din kode ved å bruke kommando-linje opsjonen -r  profile, eller fra inni kode ved å brukerequire "profile". For eksempel:

require "profile"
class Peter
  def initialize(amt)
    @value = amt
  end

  def rob(amt)     @value -= amt     amt   end end

class Paul   def initialize     @value = 0   end

  def pay(amt)     @value += amt     amt   end end

peter = Peter.new(1000) paul = Paul.new 1000.times do   paul.pay(peter.rob(10)) end

Kjør denne og du vil få noe som:

 time   seconds   seconds    calls  ms/call  ms/call  name
 33.33     0.29      0.29        1   290.00   870.00  Fixnum#times
 28.74     0.54      0.25     1000     0.25     0.31  Paul#pay
 26.44     0.77      0.23     1000     0.23     0.27  Peter#rob
  6.90     0.83      0.06     1000     0.06     0.06  Fixnum#+
  4.60     0.87      0.04     1000     0.04     0.04  Fixnum#-
  0.00     0.87      0.00        4     0.00     0.00  Module#method_added
  0.00     0.87      0.00        2     0.00     0.00  Class#new
  0.00     0.87      0.00        2     0.00     0.00  Class#inherited
  0.00     0.87      0.00        1     0.00     0.00  Peter#initialize
  0.00     0.87      0.00        1     0.00     0.00  Paul#initialize
  0.00     0.87      0.00        1     0.00   870.00  #toplevel
Med profileren kan du raskt identifisere og fikse flaskehalser. Husk å sjekk koden uten profileren etterpå---noen ganger kan senkingen profileren introduserer maskere andre problemer.

Ruby er et vidunderlig transparent og ekspressivt språk, men det tar ikke bort behovet for programmererens sunne fornuft: lage unødige objekter, gjøre unødvendig arbeid, og lage generelt overlesset kode er sløsing i alle språk.
Debug-kommandoer
b[reak] [file:]line Sett brekkpunkt på gitt linje i file (standard nåværende fil).
b[reak] [file:]name Sett brekkpunkt på method i file.
b[reak] Vis brekkpunkter og vaktpunkter.
wat[ch] expr Brekk når uttrykk er sant.
del[ete] [nnn] Slett brekkpunkt nnn (standard alle).
disp[lay] expr Vis verdi av nnn hver gang debuggeren får kontroll.
disp[lay] Vis nåværende displayer.
undisp[lay] [nnn] Fjern visning (standard hele).
c[ont] Fortsett eksekusjon.
s[tep] nnn=1 Eksekuter neste nnn linjer, stepp inn i methoder.
n[ext] nnn=1 Eksekuter neste nnn linjer, stepp over methoder.
fi[nish] Slutt eksekusjon av den nåværende funksjonen.
q[uit] Exit debuggeren.
w[here] Vis nåværende stakk-ramme.
f[rame] Synonym for where.
l[ist] [start--end] List kilde-filer fra start til slutt.
up nnn=1 Flytt opp nnn nivåer i stakk-rammen.
down nnn=1 Flytt ned nnn nivåer i stakk-rammen.
v[ar] g[lobal] Vis globale variabler.
v[ar] l[ocal] Vis lokale variabler.
v[ar] i[stance] obj Vis instansvariabler til obj.
v[ar] c[onst] Name Vis konstanter i klasse eller modul-navn.
m[ethod] i[nstance] obj Vis instansmetoder til obj.
m[ethod] Name Vis instansmetoder til klassen eller modul-navnet.
th[read] l[ist] List alle tråder.
th[read] [c[ur[rent]]] Vis status på nåværende tråd.
th[read] [c[ur[rent]]] nnn Lag tråd nnn nåtidig og stopp den.
th[read] stop nnn Lag tråd nnn nåtidig og stopp den.
th[read] resume nnn Start igjen tråd nnn.
[p] expr Evaluer expr i den nåværende kontekst.expr kan inkludere tildeling til variabler og metode-påkallinger.
empty En null-kommando repeterer siste kommando.

( In progress translation to Norwegian by NorwayRUG. $Revision: 1.9 $ )
$Log: trouble.xml,v $
Revision 1.9  2002/08/31 22:38:02  kent
Gjennomgang frem til profileren.

Revision 1.8  2002/08/31 22:24:27  kent
Gjenomgang frem til "Men det virker ikke".

Revision 1.7  2002/08/26 17:07:03  kent
Fikse tittel.

Revision 1.6  2002/08/26 16:32:49  kent
Fikset noen små problemer med TT-tagger.


Forrige < Innhold ^
Neste >

Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide".
Translation to norwegian by Norway Ruby User Group.
Copyright for the english original authored by David Thomas and Andrew Hunt:
Copyright © 2001 Addison Wesley Longman, Inc.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at
http://www.opencontent.org/openpub/).

(Please note that the license for the original has changed from the above. The above is the license of the original version that was used as a foundation for the translation efforts.)

Copyright for the norwegian translation:
Copyright © 2002 Norway Ruby User Group.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at
http://www.opencontent.org/openpub/).
Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.
Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.