|
|||
Forrige < |
Innhold ^
|
Neste >
|
ObjectSpace::each_object
.
Vi kan bruke det til å gjøre alle slags flotte triks.
For eksempel å iterere over alle objeketer av type Numeric
,
kunne du skrive det følgende.
a = 102.7 b = 95.1 ObjectSpace.each_object(Numeric) {|x| p x } |
95.1 102.7 2.718281828 3.141592654 |
Math
-modulen definerer
konstanter for e og PI; siden vi undersøker alle levende objekter i systemet
dukker disse opp også.
Men det er en hake. La oss se på det samme eksempelet med andre tall.
a = 102 b = 95 ObjectSpace.each_object(Numeric) {|x| p x } |
2.718281828 3.141592654 |
Fixnum
-objektene vi lagde kom opp. Det er fordi
ObjectSpace
ikke vet om objekter med umiddelbare verdier:
Fixnum
, true
, false
og nil
.
r = 1..10 # Lag et Range-objekt
|
||
list = r.methods
|
||
list.length
|
» |
60
|
list[0..3]
|
» |
["size", "exclude_end?", "to_s", "length"]
|
r.respond_to?("frozen?")
|
» |
true
|
r.respond_to?("hasKey")
|
» |
false
|
"me".respond_to?("==")
|
» |
true
|
num = 1
|
||
num.id
|
» |
3
|
num.class
|
» |
Fixnum
|
num.kind_of? Fixnum
|
» |
true
|
num.kind_of? Numeric
|
» |
true
|
num.instance_of? Fixnum
|
» |
true
|
num.instance_of? Numeric
|
» |
false
|
Class#superclass
.
For klasser og moduler lister
Module#ancestors
både
superklasser og moduler som er mixed-in.
klass = Fixnum begin print klass klass = klass.superclass print " < " if klass end while klass puts p Fixnum.ancestors |
Fixnum < Integer < Numeric < Object [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel] |
ObjectSpace
for å iterere over alle
Class
-objekter:
ObjectSpace.each_object(Class) do |aClass| # ... end |
class Demo
|
||
private
|
||
def privMethod
|
||
end
|
||
protected
|
||
def protMethod
|
||
end
|
||
public
|
||
def pubMethod
|
||
end
|
||
|
||
def Demo.classMethod
|
||
end
|
||
|
||
CONST = 1.23
|
||
end
|
||
|
||
Demo.private_instance_methods
|
» |
["privMethod"]
|
Demo.protected_instance_methods
|
» |
["protMethod"]
|
Demo.public_instance_methods
|
» |
["pubMethod"]
|
Demo.singleton_methods
|
» |
["classMethod"]
|
Demo.constants - Demo.superclass.constants
|
» |
["CONST"]
|
Module.constants
returnerer
alle konstantene tilgjengelig via en modul, inkludert
konstanter fra modulens superklasser. Vi er ikke interessert i
disse i øyeblikket, så vi trekker dem fra vår liste.
Gitt en liste med metode-navn, kan vi nå bli fristet til å prøve å kalle på dem.
Heldigvis er det enkelt i Ruby.
typedef struct { char *name; void (*fptr)(); } Tuple; Tuple list[]= { { "play", fptr_play }, { "stop", fptr_stop }, { "record", fptr_record }, { 0, 0 }, }; ... void dispatch(char *cmd) { int i = 0; for (; list[i].name; i++) { if (strncmp(list[i].name,cmd,strlen(cmd)) == 0) { list[i].fptr(); return; } } /* not found */ } |
commands
),
og spør det objektet om å utføre en metode kalt det samme navnet som
kommando-strengen.
commands.send(commandString) |
send
:
det virker på hvilket som helst objekt.
"John Coltrane".send(:length)
|
» |
13
|
"Miles Davis".send("sub", /iles/, '.')
|
» |
"M. Davis"
|
Method
-objekter.
Et Method
-objekt har mye felles med et Proc
-object:
det representerer en kodebit og en kontekst som det skal kjøres i.
I dette tilfellet er koden som skal kjøres metodekroppen og konteksten er objektet som lagde metoden. Når vi har fått tak i et Method
-objekt kan vi kalle det på et
senere tidspunkt ved å sende beskjeden call
.
trane = "John Coltrane".method(:length)
|
||
miles = "Miles Davis".method("sub")
|
||
|
||
trane.call
|
» |
13
|
miles.call(/iles/, '.')
|
» |
"M. Davis"
|
Method
-objektet rundt omkring slik som med et hvert annet objekt, og når du kaller
Method#call
, blir metoden kjørt på akkurat samme måte som om du hadde kalt den direkte på det opprinnelige objektet. Det er som om man har en funksjonspeker i C, men gjennomført i en objekt-orientert stil.
Du kan også bruke Method
-objekter med iteratorer.
def double(a)
|
||
2*a
|
||
end
|
||
|
||
mObj = method(:double)
|
||
|
||
[ 1, 3, 5, 7 ].collect(&mObj)
|
» |
[2, 6, 10, 14]
|
eval
-metoden
(samt dens varianter, slik som class_eval
, module_eval
og instance_eval
) parser og utfører en vilkårlig streng som inneholder gyldig Ruby-kode.
trane = %q{"John Coltrane".length}
|
||
miles = %q{"Miles Davis".sub(/iles/, '.')}
|
||
|
||
eval trane
|
» |
13
|
eval miles
|
» |
"M. Davis"
|
eval
kan det være behjelpelig å angi eksplisitt hvilken kontekst uttrykket skal evalueres innenfor, i stedet for å bruke den gjeldende konteksten. Du kan få tak i en kontekst ved å kalle
Kernel#binding
på det ønskede punktet i koden.
class CoinSlot def initialize(amt=Cents.new(25)) @amt = amt $here = binding end end a = CoinSlot.new eval "puts @amt", $here eval "puts @amt" |
$0.25USD nil |
eval
evaluerer uttrykket @amt
i konteksten til instansen av klassen CoinSlot
. Det andre eval
-kallet evaluerer @amt
i konteksten til Object
hvor instansvariabelen @amt
ikke er definert.
Object#send
,
Method#call
og de forskjellige variasjonene av eval
.
Hvilken av teknikkene du foretrekker kan avhenge av dine behov, men vær oppmekrsom på at eval
er betydelig tregere enn de andre (eller, for mer optimistiske lesere, send
og call
er betydelig raskere enn eval
).
require "benchmark" # fra RAA, Ruby sin applikasjonsarkiv include Benchmark test = "Stormy Weather" m = test.method(:length) n = 100000 bm(12) {|x| x.report("call") { n.times { m.call } } x.report("send") { n.times { test.send(:length) } } x.report("eval") { n.times { eval "test.length" } } } |
user system total real call 0.200000 0.000000 0.200000 ( 0.206547) send 0.240000 0.000000 0.240000 ( 0.232178) eval 2.570000 0.000000 2.570000 ( 2.560201) |
Kernel::system
[Denne Eiffel-inspirerte eiendommeligheten
med å omdøpe en fasilitet og redefinere en ny en kan være veldig nytt,
men det kan også forårsake problemer. Dersom en subklasse gjør det samme
og omdøper metodene med de samme navnene kan du ende opp med en
uendelig løkke. Du kan unngå dette ved å bruke et unikt symbolnavn
eller en konsekvent navngivningskonvensjon når du omdøper metoder.
]
og bytt den ut med din egen som både logger kommandoen og kaller den
opprinnelige Kernel
-metoden.
module Kernel alias_method :old_system, :system def system(*args) result = old_system(*args) puts "system(#{args.join(', ')}) returned #{result}" result end end system("date") system("kangaroo", "-hop 10", "skippy") |
Sun Nov 25 23:45:40 CST 2001 system(date) returned true system(kangaroo, -hop 10, skippy) returned false |
Class#new
, metoden som blir kalt for å få plass til et nytt objekt.
Teknikken er ikke perfekt---noen innebygde objekter, slik som strenger skrevet direkte i kildekoden, blir konstruert uten et kall til new
---men
det vil fungere helt fint for objekter vi skriver.
class Class alias_method :old_new, :new def new(*args) result = old_new(*args) result.timestamp = Time.now return result end end |
Object
-klassen.
class Object def timestamp return @timestamp end def timestamp=(aTime) @timestamp = aTime end end |
class Test
|
||
end
|
||
|
||
obj1 = Test.new
|
||
sleep 2
|
||
obj2 = Test.new
|
||
|
||
obj1.timestamp
|
» |
Sun Nov 25 23:45:40 CST 2001
|
obj2.timestamp
|
» |
Sun Nov 25 23:45:42 CST 2001
|
Hendelse | Tilbakekallsmetode | |||||||
En instansmetode legges til |
Module#method_added
|
|||||||
En singleton-metode legges til |
Kernel::singleton_method_added
|
|||||||
En subklasse blir opprettet |
Class#inherited
|
|||||||
En modul blir mikset |
Module#extend_object
|
|||||||
set_trace_func
kjører en Proc
med allverdens saftig debuggingsinformasjon
hver gang en ny kildekodelinje blir utført, metoder kalles, objekter lages,
og så videre. Du finner en fullstendig beskrivelse på
side 422, men her er en liten smakebit.
class Test def test a = 1 b = 2 end end set_trace_func proc { |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname } t = Test.new t.test |
line prog.rb:11 false c-call prog.rb:11 new Class c-call prog.rb:11 initialize Object c-return prog.rb:11 initialize Object c-return prog.rb:11 new Class line prog.rb:12 false call prog.rb:2 test Test line prog.rb:3 test Test line prog.rb:4 test Test return prog.rb:4 test Test |
trace_var
(beskrevet på
side 427)
som lar deg legge til en kobling til en global variabel; hver gang
en verdi tilordnes til den globale variabelen, blir ditt Proc
-objekt kalt.
caller
,
som returnerer en Array
av
String
-objekter som representerer den
gjeldende kallstakken.
def catA puts caller.join("\n") end def catB catA end def catC catB end catC |
prog.rb:5:in `catB' prog.rb:8:in `catC' prog.rb:10 |
Marshal::dump
. Det man ofte gjør, er å dumpe et fullstendig objekttre som starter med et gitt objekt. Senere kan du hente frem objektet igjen ved
å bruke
Marshal::load
.
Her følger et kort eksempel. Vi har en klasse Chord
som holder en samling av musikknoter. Vi ønsker å lagre en spesielt fin
akkord slik at våre barnebarn kan laste den inn i Ruby versjon 23.5
og nyte den, de også. La oss starte med klassene til
Note
og Chord
.
class Note attr :value def initialize(val) @value = val end def to_s @value.to_s end end class Chord def initialize(arr) @arr = arr end def play @arr.join('-') end end |
Marshal::dump
for å lagre en serialisert versjon av den til disk.
c = Chord.new( [ Note.new("G"), Note.new("Bb"), Note.new("Db"), Note.new("E") ] ) File.open("posterity", "w+") do |f| Marshal.dump(c, f) end |
File.open("posterity") do |f|
|
||
chord = Marshal.load(f)
|
||
end
|
||
|
||
chord.play
|
» |
"G-Bb-Db-E"
|
IO
og
singleton-objekter kan ikke lagres utenfor det kjørende Ruby-miljøet
(et TypeError
-unntak heves dersom du forsøker).
Men selv om ditt objekt ikke inneholder en av disse problematiske objekttypene
kan du ha behov for å ta kontroll over serialiseringsprosessen selv.
Marshal
tilbyr de koblingene du trenger.
I objektene som krever egendefinert serialisering implementerer du bare
to metoder: en instansmetode kalt _dump
, som skriver objektet
ut til en streng, og en klassemetode kalt _load
, som leser
en streng du har laget tidligere og omformer den til et nytt objekt.
Til eksempel følger en klasse som definerer sin egen serialisering.
Av en eller annen grunn vil Special
ikke lagre en
av sine interne datamedlemmer, ``@volatile
''.
class Special def initialize(valuable) @valuable = valuable @volatile = "Goodbye" end def _dump(depth) @valuable.to_str end def Special._load(str) result = Special.new(str); end def to_s "#{@valuable} and #{@volatile}" end end a = Special.new("Hello, World") data = Marshal.dump(a) obj = Marshal.load(data) puts obj |
Hello, World and Goodbye |
Marshal
som starter på side 428.
join
på drb-tråden før du avslutter programmet ditt.
require 'drb' class TestServer def doit "Hello, Distributed World" end end aServerObject = TestServer.new DRb.start_service('druby://localhost:9000', aServerObject) DRb.thread.join # Vennligst ikke avslutt ennå! |
require 'drb' DRb.start_service() obj = DRbObject.new(nil, 'druby://localhost:9000') # Now use obj p obj.doit |
doit
,
som returnerer en streng som klienten printer ut:
"Hello, Distributed World" |
nil
-argumentet til DRbObject
indikerer at vi vil
knyttes til et nytt distribuert objekt. Vi kunne også bruke et eksisterende objekt.
Du kan humre over det. Det lyder som Javas RMI, eller CORBA, eller hva som helst.
Ja, det er en funksjonell distribuert objekt-mekanisme---men det er skrevet i kun
200 linjer med Ruby-kode. Ingen C, ikke noe fancy, bare rett-fram Ruby-kode.
Selvfølgelig, det er ikke noen navngivings- eller utvekslingstjeneste,
eller noe annet du kan se i CORBA, men det er enkelt og forholdsvis raskt. På
vårt 233 MHz test-system, kjørte denne eksempel-koden på ca. 50 fjernbeskjed-kall
pr. sekund.
Og hvis du liker Suns JavaSpaces, basisen for deres JINI-arkitektur,
vil du være interessert i å vite at drb er distribuert med en kort modul som
gjør omtrent det samme. JavaSpaces er basert på en teknologi som heter Linda.
For å bevise at den japanske forfatteren av modulen har humoristisk sans,
kalles Rubys versjon "rinda."
public
til private
, og så videre. Du kan til og med
forandre grunnleggende typer, slik som Class
og Object
.
Med en gang du blir vant til denne fleksibiliteten er den hardt å gå tilbake til
et statisk språk som C++, eller til og med et halv-statisk språk slik som Java.
Men, hvorfor skulle du ville det?
( In progress translation to Norwegian by NorwayRUG. $Revision: 1.6 $ )
$Log: ospace.xml,v $ Revision 1.6 2003/07/01 11:40:46 kent Gjort ferdig første kladd.
Forrige < |
Innhold ^
|
Neste >
|