4. Variabelen, constanten en parameters
4.1 Maakt een toekenning een nieuwe copie van een object?
Alle variabelen en constanten verwijzen naar een object.
Uitzondering
zijn niet geïnitialiseerde locale variabelen, die verwijzen
nergens
naar. Het gebruik van dat soort variabelen levert een NameError
exceptie op. Bij het doen van een toekenning aan een variabele of het
initialiseren
van een constante laat je deze verwijzen naar het object van je keuze.
Het doen van een toekenning maakt dus nooit een nieuwe copie van een object.
Voor enkele speciale gevallen is er wat meer uitleg nodig.
Instanties van
Fixnum, NilClass, TrueClass, and FalseClass worden wel direct, dus
zonder
verwijzing, in variabelen en constanten gezet. Een variabele met als
waarde
het getal 42 of de constante true
bevatten echt deze
waarde en
dus geen verwijzing. Toewijzing leid in dit geval dus tot een fysieke
copie
van deze objecten of types. Dit wordt uitgebreider bespoken in Immediate and Reference Objects.
4.2 Wat is de scope van een locale variabele?
Eeen nieuwe scope voor een locale variabele wordt gemaakt in (1) hoofdniveau (main), (2) een klasse (of module) definitie of (3) een methode definitie.
i = 1 # (1) |
(Merk op de de klasse definitie uitvoerbare code is: de trace boodschap die de klasse bevat wordt geprint wanneer de klasse gedefinieerd word).
Een block (``{'' ... ``}'' or do
... end
)
introduceert
bijna een nieuwe scope ;-) Locale variabelen binnen een block zijn
niet
toegengkelijk buiten het block. Echter, er wordt geen nieuwe locale
variabele
aangemaakt als een locale variabele binnen het block dezelfde naam
heeft als
een bestaande locale variabele binnen de scope van de aanroeper. De
variabele
is dan buiten het block te benaderen.
a = 0 |
Dit wordt belangrijk bij threading: iedere thread krijgt zijn eigen copie van variabelen die locaal zijn t.o.v. het block van de thread:
threads = [] |
while
, until
, en for
zijn
control
structures, geen blocks, dus locale variabelen binnen de structuur zijn
toegangkelijk
vanuit de omringende omgeving. loop
echter is a
methode
en het bijbehorende block introduceerd een nieuwe scope.
4.3 Wanneer is een locale variabelel toegangkelijk?
Eigenlijk is het beter om te vragen: "Wnneer bepaald Ruby dat iets een variabele is?" Het probleem ontstaat doordat de expressie ``a'' zowel een variabele als een methode aanroep zonder parameters kan zijn. Om een keuze te kunnen maken zoekt Ruby naar toekennings statements. Als op enig punt in de source code voorafgaand aan het gebruik van ``a'' een waarde aan ``a'' wordt toegekend wordt ``a'' gezien als variabele anders wordt het behandeld als een methode. Als voorbeeld hiervan een code fragment gemaakt door Clemens Hintze:
def a |
Tijdens het parsen ziet Ruby ``a'' gebruikt worden in het eerste print statement zonder vorafgaande toekenning en gaat er dus vanuit dat het een methode aanroep beteft. Bij het tweede print statement echter is Ruby een toekenning tegengekomen en behandeld ``a'' dus als een variabele.
Merk op dat de toekenning niet hoeft te zijn uitgevoerd: Ruby moet
alleen
een toekenning in de source gezien hebben. Dit programma veroorzaakt
geen
foutmelding:
a = 1 if false; a # -> nil |
Dit is normaal gesproken geen probleem. Mocht je er tegenaan lopen
zet
dan een toekenning zoals a = nil
voor het eerste
gebruik
van de variabele. Dit heeft bovendien als voordeel dat het de
toegangstijd
voor locale variabelen die in loops worden gebruikt versneld.
4.4 Wat is de scope of a constante?
Een constante gedefinieerd in een klasse of module is direct bereikbaar binnen die definitie.
Constanten zijn direct toegangkelijk vanuit binnen liggende geneste klassen en modules.
Constanten in superklassen and ge-include modules zijn direct toegankelijk.
Afgezien van deze gevallen kunnen klasse en module constanten worden
benaderd
met behulp van de ::
operator: ModuleNaam::CONST1
of KlasseNaam::CONST2
.
4.5 Hoe worden parameters doorgegeven?
De actuele parameter word toegekend aan de formele parameter wanneer de methode wordt aangeroepen.(Zie toekenning voor meer over de semantiek van toekenningen)
def addOne(n) |
Wanneer je object referenties doorgeeft is het mogelijk dat een methode de inhoud van een doorgegeven muteerbaar object wijzigt.
def downer(string) |
Er is geen equivalent voor de pass-by-reference semantiek van andere programmeertalen..
4.6 Veranderd toekenning van een waarde aan een formele parameter de actuele parameter?
Een formele parameter is een locale variabele. Binnen een methode veranderd leidt een toekenning alleen tot het verwijzen naar een ander object.
4.7 Wat gebeurt er als ik een methode aanroep via aan formele parameter?
Alle Ruby variabelen (inclusief methode parameters) zijn verwijzingen naar objecte. Je kan methodes van deze objecten aanroepen om de staat van het object op te vragen of te wijzigen of om het object iets te laten doen. Dit kan je ook doen met objecten die als parameter aan methodes zijn doorgegeven.Voorzichtigheid is geboden wanner je dit doet omdat er sprake kan zijn van neven effecten die maken dat het programma moeilijk te volgen wordt.
4.8 Wat betekend het als een parameter vooraf word gegaan door ``*''?
Gebruikt als formele parameter maakt het sterretje het mogelijk een
variabel
aantal parameters door te geven aan een methode door ze te verzamelen
in een
array. Het array word dan toegekend aan de parameter met het sterretje.
def foo(prefix, *all) |
Gebruikt in een methode aanroep expandeerd de *
een
array.
Waarna de individuele elementen als parameter worden doorgegeven.
arguments.
a = [1, 2, 3] |
Je kan een *
zetten voor het laatste argument van:
- De linkerzijde van een meervoudige toekenning.
- De rechterzijde van een meervoudige toekenning.
- De formele parameterlijst van een methode.
- Actuele prarameters in een method aanroep.
- In de
when
clausule van eencase
structuur.
Bijvoorbeeld:
x, *y = [7, 8, 9] |
4.9 Wat betekend het als een parameter vooraf word gegaan door ``&''?
Als de laatste formele parameter van een methode vooraf word gegaan
door
een ampersand word een block dat volgt op de methode aanroep omgezet in
een
Proc
object en toegekend aan de laatste formele parameter.
Als de laatste formele parameter van een methode een Proc
object is kan je die vooraf laten gaan door een ampersand om die om te
zetten
in een block. De methode kan dan yield
gebruiken om het
block
aan te roepen.
square = proc { |i| i*i } |
4.10 Hoe kan ik een default waarde opgeven voor een formele parameter?
def greet(p1='hello', p2='world') |
De default waarde (die een willekeurige expressie mag zijn) word geëvalueerd op het moment dat de methode wordt aangeroepen. Voor de evaluatie word de scope van de methode gebruikt.
4.11 Hoe kan ik parameters doorgeven aan een block?
The formal parameters of a block appear between vertical bars at the start of the block:
proc { |a, b| a <=> b } |
Deze parameters zijn locale variabelen. Als een locale variabele met
dezelfde
naam bestaat als het block word uitgevoerd zal die variabele gewijzigd
worden
door het aanroepen van het block. Mischien is dit iests goeds,
misschien
ook niet.
Typically, arguments are passed to a block using yield
(or an iterator that calls yield
), or by using the Proc.call
method.
4.12 Waarom is mijn object onverwachts veranderd?
A = a = b = "abc" |
Variabelen bevatten verwijzingen naar objecten.The
toekenning
A = a = b = "abc"
zet een verwijzing naar de string
``abc''
intA
, a
, en b
.
Als b.concat("d")aanroept
, word de concat
methode
van de string ``abc'' aangeroepen. De string veanderd dan van
``abc''
in ``abcd''. Omdat a
en A
verwijzen
naar dat
zelfde string object veranderd hun waarde eveneens.
In de praktijk is dit een minder groot probleem dan het hier lijkt.
Bovendien kunnen vanaf Ruby 1.5.2 alle objecten frozen worden, wat ze beschermd tegen verandering.
4.13 Kan de waarde van een constante ooit veranderen?
Een constante is een variabele waarvan de naam begint met een hoofdletter. In oudere Ruby implementaties werd er een waarschuwing gegeven als aan een constante een nieuwe waarde werd tegekend. In nieuwere Ruby versies kunnen, binnen instance methodes, constanten geen nieuwe waarde krijgen. Buiten dat kan de waarde altijd worden gewijzigd.