Logiske uttrykk skiller seg fra en del andre språk på et viktig område:
Kun nil
og false
evalueres til usant.
0, "", [], {} og andre 'tomme' objekter evaluerer til sant.
(For folk vant til C, Perl og/eller Python, er dette en av de vanligste kildene til feil.)
If-setningen byr ikke på så mange overraskelser i Ruby.
1| # Spør først om alderen. 2| print "Hvor gammel er du?: " 3| alder = gets.to_i 4| 5| if alder < 1 then puts "Nå tuller du vel?"; exit end 6| 7| if alder >= 18 8| puts "Du er myndig." 9| elsif alder >= 16 10| puts "Du er lovlig." 11| else 12| puts "Småen!" 13| end 14| 15| # 'if' kan også returnere en verdi, så du slipper 16| # å bruke '?:' operatoren hvis du ikke liker den. 17| drikkevare = 18| if alder >= 60 19| "Sviskejuice" 20| else 21| if alder >= 20 22| "Sprit" 23| elsif alder >= 18 24| "Øl og vin" 25| else 26| "Brus" 27| end 28| end 29| puts "Kjøp deg litt #{drikkevare}" |
Merk at then
nøkkelordet ikke er nødvendig når man skriver if-setningen over flere linjer. Noen ting er valgfrie i Ruby forutsatt at uttrykket ikke blir tvetydig.
get string - henter en tekststreng ifra standard input.
to integer - forsøker å gjøre objektet om til et heltall.
Mulighetene til å skrive logiske uttrykk som er nærmere hvordan vi snakker, gjør Ruby mer lesbart, men kan også forvirre.
1| print "Liker du Ruby? [ja/nei]:" 2| svar = gets.chomp.downcase 3| 4| # if kan også brukes etter uttrykk 5| puts "Jeg liker også Ruby!" if svar=="ja" 6| 7| # 'unless' er det motsatte av 'if' 8| puts "La oss kode litt Ruby." unless svar=="nei" 9| 10| # men bør brukes forsiktig 11| unless svar[0] == ?j 12| puts "Mener du at du ikke liker Ruby?" 13| else 14| puts "Doble negasjoner er forvirrende..." 15| end |
fjerner et eventuelt newline-tegn i fra slutten av strengen.
gjør store bokstaver om til små.
tallverdien til tegnet 'j'
Ruby har også case-konstruksjonen, som ofte er et bedre valg enn en rekke elsif'er mot samme variabel.
1| print "Er du gutt eller jente?: " 2| svar = gets.downcase.chomp 3| 4| # case er også kjent som switch/case i andre språk 5| case svar 6| when "intetkjønn" 7| puts "Hei!" 8| when "jente", "kvinne", "dame" 9| puts "Heisann søta!" 10| when "gutt", "mann", "herre" 11| puts "Heisann kjekken!" 12| else 13| puts "God dag herr/fru?" 14| end |
Legg merke til at en when blokk kan slå ut på flere oppgitte verdier. Man kan også bruke regulære uttrykk, Range objekter, klasser etc. Du kan også lage dine egne objekter som kan brukes her ved å implementere === operatoren, også kalt "relationship operator". (Ja, det er 3 likhetstegn.)
Hvis ikke du er fra Bergen, så har du vel falt for denne spøken en gang...
1| # while - gjenta så lenge noe er sant 2| begin 3| print "En gutt og ei jente satt i ett tre. \n" 4| print "Så falt gutten ned. \n" 5| print " Hvem satt igjen? :> " 6| svar = gets.chomp.downcase 7| end while svar.index("jenta") |
gir indeksen til hvor i strengen det gitte argumentet finnes eller nil om det ikke finnes.
1| # Litt mer gøy med matte. 2| a = nil 3| 4| # until - gjenta inntil noe blir sant 5| until a == 0 6| print "Skriv inn ett tall (0 avslutter): " 7| a = gets.to_i 8| puts "#{a} opphøyd i 2 = " + (a**2).to_s 9| puts "Kvaderatroten av #{a} = " + (Math.sqrt(a)).to_s 10| end |
Merk at når løkke-uttrykket er foran koden som skal gjentas, droppes begin-nøkkelordet.
For løkken finnes fremdeles i Ruby, men hvor Python har gjort for-løkken glupere, har Ruby gått videre og tatt i bruk Iterator-pattern'et.
1| # La oss skrive ut 3-gange-tabellen 2| tall = 3 3| 4| # Ruby har for-løkker som de fleste språk 5| for i in (1..10) 6| puts "#{i} gange #{tall} er #{i*tall}" 7| end 8| 9| # 5-gange-tabellen 10| tall = 5 11| 12| # men for-løkkens dager er talte. 13| # for-løkken over er syntaktisk sukker for 14| # følgende bruk av iterator-metoden each. 15| (1..10).each do |i| 16| puts "#{i} gange #{tall} er #{i*tall}" 17| end |
(1..10)
lager et Range-objekt, som spenner i fra og med 1, til og med 10. Dersom du ikke ønsker å inkludere 10, kan du bruke 3 punktum, for eksempel så spenner (1...10)
i fra 1, til og med 9.
Når man skal iterere over datastrukturer, blir indekser lett i veien.
Såkalte "off-by-one" feil er ganske vanlige.
Men hvorfor ikke la datastrukturen stå for itereringen?
1| personer = [ "Ola", "Per", "Jan", "Line"] 2| 3| # Den "gamle" naive måten 4| for i in (0...personer.size) 5| puts "Hei " + personer[i] 6| end 7| 8| # Alle objekter som implementerer 'each' kan itereres over 9| for person in personer 10| puts "Er #{person} tilstede?" 11| end 12| 13| # Ruby-måten: Bruke Iterator og en block 14| personer.each do |person| 15| puts "Velkommen #{person}" 16| end |
"Hva er den |person|
greia?"
Det er nesten som en argumentdeklarasjon, men ikke til en funksjon.
do |person|; end
er en block, et veldig viktig konsept i Ruby.
En block er en kode-bit, som kan motta argumenter og returnere en verdi. Der slutter likhetene med en metode.
En block holder også tak i den omliggende konteksten og bindingen. Det betyr at lokale variable er tilgjengelig i block-koden, noe som gjør den perfekt til callback, f.eks. i grafiske brukergrensesnitt.
1| # En enkel, naiv iterator metode. 2| def tell_fingre 3| yield "Tommel" 4| yield "Peke" 5| yield "Lange" 6| yield "Ringe" 7| yield "Lille" 8| end 9| 10| # Blocken har tilgang til lokale variabler. 11| postfix = "finger..." 12| 13| # Vi sender med en block når vi kaller iterator-metoden. 14| tell_fingre do |finger| 15| puts finger + postfix 16| end |
En block er ikke et objekt av effektivitetshensyn, men kan innkapsles i et Proc-objekt. (Via Proc.new, nøkkelordene proc og lambda, eller via bruk av & prefikset i argumentlista til metode-definisjonen)
Minne-håndtering i Ruby ordnes via garbage collection, men en del andre ressurser krever eksplisitt lukking. Åpne filer, database-tilkoblinger og andre ressurser som tar opp mer enn minne, har det bedre med en eksplisitt lukking. Men slikt glemmer man lett...
1| # Hent filnavn fra kommandolinjen. 2| filnavn = ARGV[0] 3| 4| # Gamle måten. 5| fil = File.open( filnavn, "r" ) 6| linjenummer = 0 7| fil.readlines.each{ |linje| 8| linjenummer +=1 9| print "#{linjenummer}: #{linje}" 10| } 11| fil.close # Lukker filen eksplisitt 12| 13| # Bruk block til ressurs styring. 14| File.open( filnavn, "r") { |fil| 15| linjenummer = 0 16| fil.readlines.each{ |linje| 17| linjenummer += 1 18| print "#{linjenummer}>: #{linje}" 19| } 20| } 21| # File.open lukker filen etter å ha kjørt koden i blocken. |
Et Proc-objekt innkapsler som oftest en block.
De kan lages via Proc.new, nøkkelordene proc og lambda, eller via bruk av & prefikset i argumentlista til metode-definisjonen.
1| # Lager ett Proc-objekt av en block. 2| p = proc {|i| 3| puts "Hei #{i}!" 4| } 5| 6| # Vi kan kalle Proc'en eksplisitt... 7| p.call('Jens') #=> "Hei Jens!" 8| 9| # Bruke den som block... 10| [1,2,3].each( &p ) 11| 12| # & prefikset gjør en evt. block om til et Proc-objekt. 13| def tar_block( a, &block ) 14| block.call( a ) 15| end 16| 17| tar_block(5){|b| 18| puts "Hallo #{b}." 19| } #=> "Hallo 5." |