|
|||
| 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 >
|