"Call by reference" in Skill/Ocean procedure

S

Stephen Greenwood

Guest
I am learning to use procedures in Skill, more particularly in Ocean.
I would like to write a procedure that can either modify variables
that have been passed ("call by reference"). Is this possible? I
didn't see a way to do that in the manual description for "procedure
()", and by experimenting I know that the default operation is "call
by value".

One alternative would be to have the procedure return multiple values,
which are then used to set multiple variables. But looking at the
manual it appears that the procedure can only return a single value.
If that's the case, then my suspicion is that the only way to return
multiple values is to return a list and to then manipulate the list.

Thoughts or comments on these questions? Thank you for your time.
Apologies if this has already been answered before, but I had no
success searching the archives.
 
Stephen Greenwood wrote, on 11/24/09 23:50:
I am learning to use procedures in Skill, more particularly in Ocean.
I would like to write a procedure that can either modify variables
that have been passed ("call by reference"). Is this possible? I
didn't see a way to do that in the manual description for "procedure
()", and by experimenting I know that the default operation is "call
by value".

One alternative would be to have the procedure return multiple values,
which are then used to set multiple variables. But looking at the
manual it appears that the procedure can only return a single value.
If that's the case, then my suspicion is that the only way to return
multiple values is to return a list and to then manipulate the list.

Thoughts or comments on these questions? Thank you for your time.
Apologies if this has already been answered before, but I had no
success searching the archives.
Hi Stephen,

Actually SKILL does pass by reference. However, you cannot change the value
pointed to by a variable, so it gives the appearance of being pass by value.

You can see that it's pass by reference if you pass a list, a defstruct, a table
and so on - all the these allow you to modify the contents directly, and so that
may allow you to achieve what you want. For example:

procedure(MYcollectSomeResults(data)
let((signal)
signal=v("/out" ?result 'tran)
data->max=ymax(signal)
data->min=ymin(signal)
data->average=average(signal)
data
)
)

Then you could do:

info=ncons(nil)
MYcollectSomeResults(info)

and then you'll be able to look at info->max, info->min etc (this is using a
"Disembodied Property List"). The list is being modified "destructively" within
the list - and consequently you can pass back multiple values.

Another way might be to use an association table (aka a "hash"). You could do
this with the same code as above, but do:

info=makeTable('myInfoTable nil)
MYcollectSomeResults(info)

info is a hash; you can do either info['max] or info->max to get the value out.
Association Tables can use anything as the key.

Returning multiple values is straightforward - you can return a list of the
different things you want to return.

Finally, another way is to pass in the names of the variables you want to pass
in (you can sort of think of this as being a pointer):

procedure(MYcollectSomeResults(maxVar minVar averageVar)
let((signal)
signal=v("/out" ?result 'tran)
set(maxVar ymax(signal))
set(minVar ymin(signal))
set(averageVar average(signal))
t
)
)

MYcollectSomeResults('myMax 'myMin 'myAverage)

and then you'll have myMax, myMin and myAverage set to the appropriate values
after it returns.

I don't really like this approach though, because it relies (sort of) on
run-time evaluation, and doesn't work (without some changes) in SKILL++ due to
it needing to know the "environment" for the variables.

I hope that's given you enough info!

Regards,

Andrew.

--
Andrew Beckett
Senior Solution Architect - Cadence Design Systems Ltd (UK)
 
On Nov 25, 2:22 pm, Andrew Beckett <andr...@DcEaLdEeTnEcTe.HcIoSm>
wrote:
Actually SKILL does pass by reference. However, you cannot change the value
pointed to by a variable, so it gives the appearance of being pass by value.
Thanks, Andrew. You're right that I used a variable as the type for my
test. The code looked something like this:

--------------------------------------------
procedure( testThisOut( x )
printf("First x = %.2f \n" x)
x = 2.0
printf("Now x = %.2f \n" x)
)

a=1.0
=> 1.0

testThisOut(a)
=> First x = 1.00
=> Now x = 2.00
=> t

a
=> 1.0
--------------------------------------------

And since variable "a" was unmodified, I concluded (wrongly) that
procedures were pass-by-value.

The three methods you mentioned contain constructs with which I'm
unfamiliar, so I will have to study and experiment with them. Before I
do so, I have a question about the first example you gave. By
experimenting, it looks to me that procedures are not scoped locally,
but have access to outside variables. I may not have the nomenclature
correct, but I found that I can define a variable in OCEAN, then
access it inside a procedure without passing it in. And it appears to
me that the procedure MYcollectSomeResults() in your example "knows"
that there is a v("/out") in this way. But I want to be able to have
an argument like v("/out") be one of the parameters of the procedure.
Is it still applicable? Perhaps I would understand more by asking,
what is the type of the object called "data"?

procedure(MYcollectSomeResults(data)
   let((signal)
     signal=v("/out" ?result 'tran)
     data->max=ymax(signal)
     data->min=ymin(signal)
     data->average=average(signal)
     data
   )
)

Then you could do:

info=ncons(nil)
MYcollectSomeResults(info)

and then you'll be able to look at info->max, info->min etc (this is using a
"Disembodied Property List"). The list is being modified "destructively" within
the list - and consequently you can pass back multiple values.
Thank you for your detailed help. Sorry for the bad assumption about
pass-by-reference... I'm an analog designer with less formal training
in programming than I need.

Best,
Stephen
 
Stephen Greenwood wrote, on 11/26/09 00:02:
On Nov 25, 2:22 pm, Andrew Beckett <andr...@DcEaLdEeTnEcTe.HcIoSm
wrote:
Actually SKILL does pass by reference. However, you cannot change the value
pointed to by a variable, so it gives the appearance of being pass by value.

Thanks, Andrew. You're right that I used a variable as the type for my
test. The code looked something like this:

--------------------------------------------
procedure( testThisOut( x )
printf("First x = %.2f \n" x)
x = 2.0
printf("Now x = %.2f \n" x)
)

a=1.0
=> 1.0

testThisOut(a)
=> First x = 1.00
=> Now x = 2.00
=> t

a
=> 1.0
--------------------------------------------

And since variable "a" was unmodified, I concluded (wrongly) that
procedures were pass-by-value.
That's understandable (and a common perception).

The three methods you mentioned contain constructs with which I'm
unfamiliar, so I will have to study and experiment with them. Before I
do so, I have a question about the first example you gave. By
experimenting, it looks to me that procedures are not scoped locally,
but have access to outside variables. I may not have the nomenclature
correct, but I found that I can define a variable in OCEAN, then
access it inside a procedure without passing it in. And it appears to
me that the procedure MYcollectSomeResults() in your example "knows"
that there is a v("/out") in this way. But I want to be able to have
an argument like v("/out") be one of the parameters of the procedure.
Is it still applicable? Perhaps I would understand more by asking,
what is the type of the object called "data"?
In this case I happened to hardcode the signal within the function, but of
course a more likely scenario would be to pass it in as an argument. You could
either pass in the signal name, or the waveform object itself.

All variables in SKILL are by default global - only if you put them within a
let() are they local (actually you can do it a few other ways too, but variables
need to be explicitly declared as local to make them local; this is different
from, say, Tcl where the default is the other way around). Also, variables are
dynamically scoped in SKILL (lexical scoping in SKILL++) which means that you
can imagine each variable as being a stack, where when declared within a let, it
pushes a new value on that variable's stack, which gets popped off when the let
returns.

data in this example is a list, or it could be a structure, or it could be a
table - there's no type checking, but the syntax within the function would work
in all three cases.

procedure(MYcollectSomeResults(data)
let((signal)
signal=v("/out" ?result 'tran)
data->max=ymax(signal)
data->min=ymin(signal)
data->average=average(signal)
data
)
)

Then you could do:

info=ncons(nil)
MYcollectSomeResults(info)

and then you'll be able to look at info->max, info->min etc (this is using a
"Disembodied Property List"). The list is being modified "destructively" within
the list - and consequently you can pass back multiple values.

Thank you for your detailed help. Sorry for the bad assumption about
pass-by-reference... I'm an analog designer with less formal training
in programming than I need.
If you wanted to pass in the signal, you could do:

procedure(MYcollectSomeResultsNew(signal data)
data->max=ymax(signal)
data->min=ymin(signal)
data->average=average(signal)
; this is the return value - the whole list
data
)

Then you could do:

info=list(nil)
out=v("/out" ?result 'tran)
MYcollectSomeResultsNew(out info)

and then look at the variable info when it returns - you'll see that the list
now has some key-value pairs within it.

I suggest you read the "SKILL Language User Guide"
<instDir>/doc/sklanguser/sklanguser.pdf as this will give you a good overview of
the language, step by step. It's written in the style of a book on a programming
language you might buy in a bookshop, so it's pretty readable.

Regards,

Andrew.


--
Andrew Beckett
Senior Solution Architect - Cadence Design Systems Ltd (UK)
 
On Nov 26, 3:12 am, Andrew Beckett <andr...@DcEaLdEeTnEcTe.HcIoSm>
wrote:

In this case I happened to hardcode the signal within the function, but of
course a more likely scenario would be to pass it in as an argument. You could
either pass in the signal name, or the waveform object itself.

All variables in SKILL are by default global - only if you put them within a
let() are they local (actually you can do it a few other ways too, but variables
need to be explicitly declared as local to make them local; this is different
from, say, Tcl where the default is the other way around). Also, variables are
dynamically scoped in SKILL (lexical scoping in SKILL++) which means that you
can imagine each variable as being a stack, where when declared within a let, it
pushes a new value on that variable's stack, which gets popped off when the let
returns.

data in this example is a list, or it could be a structure, or it could be a
table - there's no type checking, but the syntax within the function would work
in all three cases.





procedure(MYcollectSomeResults(data)
   let((signal)
     signal=v("/out" ?result 'tran)
     data->max=ymax(signal)
     data->min=ymin(signal)
     data->average=average(signal)
     data
   )
)

Then you could do:

info=ncons(nil)
MYcollectSomeResults(info)

and then you'll be able to look at info->max, info->min etc (this is using a
"Disembodied Property List"). The list is being modified "destructively" within
the list - and consequently you can pass back multiple values.

Thank you for your detailed help. Sorry for the bad assumption about
pass-by-reference... I'm an analog designer with less formal training
in programming than I need.

If you wanted to pass in the signal, you could do:

procedure(MYcollectSomeResultsNew(signal data)
     data->max=ymax(signal)
     data->min=ymin(signal)
     data->average=average(signal)
     ; this is the return value - the whole list
     data
)

Then you could do:

info=list(nil)
out=v("/out" ?result 'tran)
MYcollectSomeResultsNew(out info)

and then look at the variable info when it returns - you'll see that the list
now has some key-value pairs within it.
Thanks, Andrew, for the clarifications. This helps a lot.

I suggest you read the "SKILL Language User Guide"
instDir>/doc/sklanguser/sklanguser.pdf as this will give you a good overview of
the language, step by step. It's written in the style of a book on a programming
language you might buy in a bookshop, so it's pretty readable.
Again, thank you. Until now I've been referring primarily to the
reference manuals (sklangref.pdf and skartistref.pdf) rather than the
user guides.

Best Regards,
Stephen Greenwood
 

Welcome to EDABoard.com

Sponsor

Back
Top