1.4.1.11. fejezet, Szimbólumok

A szimbólumok :valami formában definiálhatók. Szöveg jellege ellenére inkább hasonlít a Fixnum tipushoz, mert egyedi azonosítóval rendelkezik, az object_id-n keresztül.

puts( :helloworld.equal?( :helloworld ) ) #=> true 
puts( "helloworld".equal?( "helloworld" ) ) #=> false 
puts( 1.equal?( 1 ) ) #=> true

Az objektum tulajdonság elérhetőségét szabályzó kifejezésekben láttunk először szimbólumot:

attr_reader( :description )
attr_writer( :description )
attr_accessor( :name )

Case elágazásokban is használható:

case doThis 
  when :deletefiles : puts( 'Now deleting files...') 
  when :formatdisk  : puts( 'Now formatting disk...') 
  else puts( "Sorry, command not understood." ) 
end

Nézzük meg, mire is használható egy szimbólum.

module One
	class Fred
	end
	$f1 = :Fred
	def self.evalFred( aSymbol )
		puts( eval( aSymbol.id2name ) )	
	end
end
 
module Two
	Fred = 1
	$f2 = :Fred
	def self.evalFred( aSymbol )
		puts( eval( aSymbol.id2name ) )		
	end
end
 
def Fred()
	puts( "hello from the Fred method" )
end
 
$f3 = :Fred
 
One::evalFred( $f1 )
Two::evalFred( $f2 ) 
method($f3).call 
 
puts
 
One::evalFred( $f3 )
Two::evalFred( $f1 ) 
method($f2).call 

Eredmények:

One::Fred
1
hello from the Fred method
 
One::Fred
1
hello from the Fred method

Itt látható, hogy a szimbólum hatásköre hogyan változik, melyik modulra, metódusra, változóra hivatkozhatunk a szimbólumokkal az eval metódus felhasználásával. Modulokról a 12. fejezetben lesz még szó, és a dinamikus programozásnál bővebben megvizsgáljuk majd az eval-t. Most elégedjünk meg azzal, hogy string objektumokat értékel ki, a program futása közben összeállított utasításokat hajtathatunk vele végre. És most lepődhetünk meg igazán:

puts( $f1.object_id )
puts( $f2.object_id )
puts( $f3.object_id )

ugyanis mind a három globális változó object_id-ja egyező. Más szóval a module One-ban a :Fred szimbólum a Fred osztályra hivatkozik, a module Two-ban a Fred=1 konstansra, és végül a main hatókörében (szkópban) a Fred metódusra, viszont mindegyik metódushívás ugyan azt a szimbólumot használta.

Szimbólumok és változók

Definiáljunk két változót:

x = 1
xsymbol = :x

És végezzük el ismét az eval kiértékelést:

amethod( eval(:x.id2name))

Az id2name a Symbol osztály metódusa. A to_s metódusa ennek az osztálynak ugyanazt teszi, mint az id2name: szöveg alakítja a szimbólum nevét. Az x szimbólumból szöveggé alakított string-et az eval kiértékeli, és úgy látja, hogy egy x változót kell végrehajtania. Vagyis az amethod egy 1-es értéket kap argumentumként.

Most nézzük meg változókkal.

def amethod( somearg )
   p( somearg )
   # puts( "[class=#{somearg.class}]\n" ) #<= uncomment to verify classes
end
 
 
x = 1 # try changing this value to 100 or "hello world"
xsymbol = :x
 
puts( '- Test #1 ------------' )
puts('amethod( x )')
amethod( x )
puts('amethod( :x )')
amethod( :x )
 
puts( '- Test #2 ------------' )
puts('amethod( eval(:x.id2name))')
amethod( eval(:x.id2name) )
 
puts( '- Test #3 ------------' )
amethod( xsymbol ) 
amethod( :xsymbol )
amethod( eval(:xsymbol.id2name))
amethod( eval( ( eval(:xsymbol.id2name)).id2name ) )
 
puts( '- Test #4 ------------' )
method(:amethod).call("")
method(:amethod).call(eval(:x.id2name))

Az eredmények pedig:

- Test #1 ------------
amethod( x )
1
amethod( :x )
:x
- Test #2 ------------
amethod( eval(:x.id2name))
1
- Test #3 ------------
:x
:xsymbol
:x
1
- Test #4 ------------
""
1

Ha ez kicsit bonyolult, nézzük meg az alábbiakat:

def mymethod( somearg ) 
  print( "I say: " << somearg ) 
end
 
this_is_a_method_name = method(:mymethod)

Itt a method(:mymethod) a :mymethod szimbólum alapján keres egy metódust. Ha talál ilyet, visszatér a megfelelő nevű Method objektummal. És ha ez tényleg egy metódus, miért ne hívhatnánk meg?

this_is_a_method_name.call( "hello world" ) #=> Ezt írja ki: I say: hello world

Miért használjunk szimbólumokat?

A szimbólumoknak speciális helye van a dinamikus programozásban. Például új metódus készíthető vele:

class Array
  define_method( :aNewMethod, lambda{ |*args| puts( args.inspect) } )
end

Aztán le is ellenőrizhető, hogy létrejött:

Array.method_defined?( :aNewMethod ) #=> returns: true

amit aztán meg is hívhatunk:

[].aNewMethod( 1,2,3 ) #=> returns: [1,2,3]

El is távolíthatjuk a metódust, ha nincs rá szükségünk

class Array 
  remove_method( :aNewMethod )
end

A dinamikus programozásra nagymértékben támaszkodik a Rails.

Közelebbről megvizsgálva

Egy szimbólum, lényegében egy mutató a szimbólum táblázatra. A szimbólum tábla egy belső lista a Ruby-ban definiált azonosítókról, mint a változó vagy metódus név. Ha közelebbről megnézzük a Ruby-ban definiált szimbólumokat, használjuk az alábbi utasítást:

p( Symbol.all_symbols )

Egy csomó szimbólumot kapunk, amit részleteiben is megnézhetünk, hiszen egy tömbről van szó, habár sorba nem rendezhető, mert ezt a metódust nem örökölte. Azért megoldható ez is, ha string-é alakítjuk mindet, és egy tömbbe tároljuk a string-eket, ami máris sorba rendezhető. Az alábbiakban pont ezt csináljuk.

str_arr = Symbol.all_symbols.collect{ |s| s.to_s }
puts( str_arr.sort )