Answer: No Option is correct.
The answer to this question can be found in Example $6$ below.
The "Call by name" parameter passing technique is used by imperative languages like Algol $W,$ and is used by several functional languages like Haskell.
We'll see how the "Call by name" parameter passing technique works (theoretical idea for understanding its working), Not how it is actually implemented(practical implementation).
How "Call by name" works (Idea of this technique):
In general, the effect of pass-by-name is to textually substitute the argument expressions(actual parameters) in a procedure call for the corresponding parameters(formal parameters) in the body of the called procedure.*
i.e. Direct Substitution of actual parameters in the place of the formal parameter in the called procedure.*
(* means that it is not a complete definition/statement and some technical details are missing and as we go on, we'll fill in these details.)
Example 1: What will be the output if the "Call by name" parameter passing technique is used.
Given Program :
void P(x) {
print(x+10);
print (x);
}
main()
{
int j = 10;
P(j);
}
Answer: Since the "Call by name" parameter passing technique is used, we can re-write the program as follows:
void P(x) {
print(j+10);
print (j);
}
main()
{
int j = 10;
P(j);
}
Hence, output $: 20,10$
NOTE 1 (Technical Detail 1) : It does Not matter which Scoping is used(static or dynamic),
once we substitute actual arguments in the place of formal parameters in the called function,
for those variables in the actual arguments, environment of caller function will be applicable.
Hence, in the above example $1,$ in function $P, j$ refers to the $j$ of the caller function i.e. main function.
Example $2$ will illustrate Note $1.$
Example 2: What will be the output if the "Call by name" parameter passing technique is used in the case of static and dynamic scoping?
global int j=100;
void P(x) {
print(x+10);
print (x);
}
main()
{
int j = 10;
P(j);
}
Answer: Since the "Call by name" parameter passing technique is used, we can re-write the program as follows:
global int j=100;
void P(x) {
print(j+10);
print (j);
}
main()
{
int j = 10;
P(j);
}
- In case of Dynamic scoping $: 20,10 $
- In case of Static scoping $: 20,10 $
Note that in the case of Static scoping, $j$ in function $P$ does not refer to the global variable But $j$ refers to the caller function, more precisely, Once we substitute Actual arguments in the place of formal parameters in the called function, for those variables in the actual arguments, Environment of caller function will be applicable. Hence, $j$ in $P$ will be accessed/updated according to caller function's environment, and since, caller function i.e. main function here, has a local variable $j$, so, this $j$ will be accessed/updated by the function $P.$
Example 3: What will be the output if the "Call by name" parameter passing technique is used in the case of static and dynamic scoping?
global int j=100;
void P(x) {
print(x+10);
print (x);
}
main()
{
P(j);
}
Answer: Since the "Call by name" parameter passing technique is used, we can re-write the program as follows:
global int j=100;
void P(x) {
print(j+10);
print (j);
}
main()
{
P(j);
}
- In case of Dynamic scoping $: 110,100 $
- In case of Static scoping $: 110,100$
Again, once we substitute actual arguments in the place of formal parameters in the called function, for those variables in the actual arguments, environment of caller function will be applicable. Hence, $j$ in $P$ will be accessed/updated according to the caller function's environment and since, caller function i.e., main function here, does not have a local variable $j$, environment of function main for variable $j$ would depend on the scoping used, But this scoping will be seen from the perspective of caller function i.e. function main, not from the perspective of called function i.e. function P. So, it is like accessing variable $j$ in the main function, not in $P$ function. Hence, for this example $3,$ in both scoping, the main function will be using Global variable $j.$
Example 4: What will be the output if "Call by name" parameter passing technique is used in the case of static and dynamic scopings?
global int j = 100, i = 300;
void Q(x) {
print(x+10);
print (x); }
void P(x) {
print(x+10);
Q(i);
print (x);
}
main()
{
int i = 500;
int j = 10;
P(j);
}
Answer: Since "Call by name" parameter passing technique is used, we can re-write the program as follows:
global int j = 100, i = 300;
void Q(x) {
print(i+10); // this i refers to i in the caller function i.e. P function's environment
print (i); } // this i refers to i in the caller function i.e. P function's environment
void P(x) {
print(j+10); // this j refers to j in the caller function i.e. main function's environment
Q(i);
print (j); // this j refers to j in the caller function i.e. main function's environment
}
main()
{
int i = 500;
int j = 10;
P(j);
}
- In case of Dynamic scoping $: 20,510,500,10$
- In case of Static scoping $: 20,310,300,10$
In Static scoping, variable $i$ in $P$ function's environment refers to the global variable $i$. In Dynamic scoping, variable $i$ in $P$ function's environment refers to the main function's $i.$
NOTE 2 (Technical Detail 2) :
if any of the local variables in the called procedure clash with the caller's variables, they (called function's clashing variables) must be renamed uniquely before substitution.
Example 5: What will be the output if the "Call by name" parameter passing technique is used?
Given Program :
void P(x) {
int j=100;
print(x+10);
print(j);
print (x);
}
main()
{
int j = 10;
P(j);
}
Answer: Since "Call by name" parameter passing technique is used, we can re-write the program as follows:
Caller function's actual argument contains variable $j$ which clashes with called function $P$'s local variable $j,$ hence, we rename called function $P$'s local variable $j$ and change it to $j'.$
void P(x) {
int j'=100;
print(j+10); // this j refers to j in the caller function i.e. main function's environment
print(j'); // this j' refers to the local variable j' in P.
print (j); // this j refers to j in the caller function i.e. main function's environment
}
main()
{
int j = 10;
P(j);
}
Hence, output $: 20,100, 10.$
Coming to the actual GATE question, we'll call it to example $6.$
Example 6: What will be the output if the "Call by name" parameter passing technique is used, in the case of static and dynamic scopings?
global int i=100, j=5;
void P(x) {
int i=10;
print(x+10);
i=200;
j=20;
print (x);
}
main() {P(i+j);}
Answer: Since the "Call by name" parameter passing technique is used, we can re-write the program as follows:
Caller function's Actual argument contains variable $i$ which clashes with called function $P$'s local variable $i,$ hence, we rename called function $P$'s local variable $i$ and change it to $i'.$
global int i=100, j=5;
void P(x) {
int i'=10; // this i' refers to the local variable i' in function P.
print(i+j+10); // this i,j refers to i,j in the caller function i.e. main function's environment
i'=200; // this i' refers to the local variable i' in function P.
j=20; // this j refers to j in the caller function i.e. main function's environment
print (i+j); // this i,j refers to i,j in the caller function i.e. main function's environment
}
main() {P(i+j);}
- In case of Static scoping $: 115, 120$
- In case of Dynamic scoping$: 115, 120$
Note that there are no local variable $i,j$ in the main function, so, when we say that $i,j$ refer to the $i,j$ in the main's environment, we mean that If $i,j$ were accessed/updated in the main function then depending on the scoping, which $i,j$ would they refer.
Here, in this question, in both static and dynamic scoping cases, $i,j$ will refer to the Global variables.
And in function $P$, in the $4$th statement $(\text{i.e.}\; j = 20)$, the Global variable $j$ will be updated.
Hence, No Option is correct for the actual above GATE question.
Example 7: What will be the output if the "Call by name" parameter passing technique is used in the case of static and dynamic scopings?
global int j = 100, i = 300;
void Q(x) {
print(x+10);
print (x); }
void P(x) {
int i = 400;
int j = 600;
print(x+10);
Q(i);
Q(j);
print (x);
}
main()
{
int i =500;
Q(i);
int j =10;
P(j);
}
Answer :
in both scoping, for this question, output $: 510,500,20,410,400,610,600,10.$
Note that When main calls $P(j)$, then Caller function main's Actual argument contains variable $j$ which clashes with called function $P$'s local variable $j,$ hence, we rename called function $P$'s local variable $j$ and change it to $j'.$ So, in function P, the 5th statement becomes $Q(j').$
Also note that when main calls $Q(i)$, then $x$ in $Q$ is replaced with $i.$ When $P$ calls $Q(i),$ then $x$ in $Q$ is replaced with $i.$ When $P$ calls $Q(j')$, then $x$ in $Q$ is replaced with $j'.$
Note that "Direct Substitution of actual parameters in the place of the formal parameter in the called procedure" is only the Idea of Call-by-name, Not the actual practical implementation. The compiler does Not do Direct Substitution blindly. Pass-by-name is difficult to implement. Argument expressions must be compiled to special parameter-less procedures called thunks. These thunks are passed into the called procedure and used whenever necessary to evaluate or re-evaluate the argument.
But we do not need to go into practical implementation details because the Idea remains the same. Hence, we can solve all the questions using the above idea of call-by-name.
NOTE 3 (Technical Detail 3) :
if any of the variables in the called procedure clash with the caller's variables, they(called function's clashing variables) must be renamed uniquely before substitution.
Clashing variables need not be local variables of the called function.
But remember that when we rename a variable, we don't really rename it. We rename it just to eliminate the possibility of confusion. So, when we rename $j $ to $j'$, we must not forget that $j'$ is actually $j$ only in the first place.
The following example will illustrate this point :
Example 8: What will be the output if the "Call by name" parameter passing technique is used in the case of static and dynamic scoping?
global int j=100, i = 300;
void Q(x) {
print(i);
print(j);
print(x+10);
print (x); }
void P(x) {
int i = 400;
int j = 600;
print(x+10);
Q(i);
Q(j);
print (x);
}
main()
{
int i =500;
int j =10;
Q(i);
P(j);
}
Answer :
- In case of Static scoping $: 300, 100, 510, 500, 20, 300, 100, 410, 400, 300, 100, 610, 600, 10$
- In case of Dynamic scoping $: 500, 10, 510, 500, 20, 400, 600, 410, 400, 400, 600, 610, 600, 10$
global int j = 100, i = 300;
void Q(x) {
print(i); // this i refers to the Global i in case of static scoping and in case of dynamic scoping, according to the calling function in the stack.
print(j); // this j refers to the Global j in case of static scoping and in case of dynamic scoping, according to the calling function in the stack.
print(x+10); // variables that are substituted here, refer to the corresponding variables in the caller function's environment
print (x); } // variables that are substituted here, refer to the corresponding variables in the caller function's environment
void P(x) {
int i = 400;
int j = 600;
print(x+10); // variables that are substituted here, refer to the corresponding variables in the caller function's environment
Q(i);
Q(j);
print (x); // variables that are substituted here, refer to the corresponding variables in the caller function's environment
}
main()
{
int i =500;
int j =10;
Q(i);
P(j);
}
When main calls $Q(i) ,$ we substitute $i$ in place of $x$ and it(Q) becomes:
void Q(x) {
print(i′); // This is renamed as i′ and it refers to
//global variable i in case of static scoping and
//in case of dynamic scoping it refers to
//variable i in main function.
print(j);
print(i+10);
print (i);
}
Hence, the two different $i's$ should be distinguished properly.