When Pass a Variable to a Function, Why the Function Only Gets a Duplicate of the Variable

When pass a variable to a function, why the function only gets a duplicate of the variable?

The are basically two schools of thought on this matter.

The first is pass-by-value where a copy of the value is created for the called function.

The second is pass-by-reference where the parameter that appears in the called function is an "alias" of the original. That means changes you make to it are reflected in the original.

C is generally a pass-by-value language. You can emulate pass-by-reference by passing the address of a variable and then using that to modify the original:

void setTo42 (int *x) { *x = 42; }
:
int y;
setTo42 (&y);
// y is now 42

but that's more passing the pointer to a variable by value, than passing the variable itself by reference.

C++ has true reference types, possibly because so many people have trouble with C pointers :-) They're done as follows:

void setTo42 (int &x) { x = 42; }

:
int y;
setTo42 (y);
// y is now 42

Pass-by-value is usually preferable since it limits the effects that a function can have on the "outside world" - encapsulation, modularity and localisation of effect is usually a good thing.

Being able to arbitrarily modify any parameters passed in would be nearly as bad as global variables in terms on modularity and code management.

However, sometimes you need pass-by-reference since it might make sense to change one of the variables passed in.

R: preventing copies when passing a variable into a function

pryr::address passes an unevaluated symbol to an internal function that returns its address in the parent.frame():

pryr::address
#function (x)
#{
# address2(check_name(substitute(x)), parent.frame())
#}
#<environment: namespace:pryr>

Wrapping of the above function can lead to returning address of a "promise". To illustrate we can simulate pryr::address's functionality as:

ff = inline::cfunction(sig = c(x = "symbol", env = "environment"), body = '
SEXP xx = findVar(x, env);

Rprintf("%s at %p\\n", type2char(TYPEOF(xx)), xx);

if(TYPEOF(xx) == PROMSXP) {
SEXP pr = eval(PRCODE(xx), PRENV(xx));
Rprintf("\tvalue: %s at %p\\n", type2char(TYPEOF(pr)), pr);
}

return(R_NilValue);
')
wrap1 = function(x) ff(substitute(x), parent.frame())

where wrap1 is an equivalent of pryr::address.

Now:

x = 1:5

.Internal(inspect(x))
#@256ba60 13 INTSXP g0c3 [NAM(1)] (len=5, tl=0) 1,2,3,4,5

pryr::address(x)
#[1] "0x256ba60"

wrap1(x)
#integer at 0x0256ba60
#NULL

with further wrapping, we can see that a "promise" object is being constructed while the value is not copied:

wrap2 = function(x) wrap1(x)

wrap2(x)
#promise at 0x0793f1d4
# value: integer at 0x0256ba60
#NULL

wrap2(x)
#promise at 0x0793edc8
# value: integer at 0x0256ba60
#NULL

# wrap 'pryr::address' like your 'bar'
( function(x) pryr::address(x) )(x)
#[1] "0x7978a64"

( function(x) pryr::address(x) )(x)
#[1] "0x79797b8"

Python: Keep changes on a variable made within a function

This is an example of passing variables to functions by value. By default, when you pass a variable to a function in Python, it is passed by value.

What it means is, that a new variable with a new scope is created with the same value of x. So, any change that happens to the new x is not reflected to the x outside the function's scope.

If you want to get the value from the function back, you can use the return statement (as you have used). return returns the value back from the function. However, in your example there is no variable to receive it. Hence, it is lost.

You would have to call the function as x = test(x). This ensures that x receives the value back from the function.

avoid creating duplicate variable names in function

Thanks to @G. Grothendieck and @Limey, the following simplification works (pred_fun_final) although I do get a warning message.

#original function in OP
pred_fun_original <- function(time_to_sur, model) {

pred_data <- ds[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()

ds$pred_var_tmp <-
rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv

#rename variable
pred_var <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
names(ds)[names(ds) == "pred_var_tmp"] <- pred_var

assign("ds", ds, env = .GlobalEnv)
}
pred_fun_original(time_to_sur = 0.2, fit)

#save created variable
test1 <- ds$pred_prob_0.2_rms

#remove pred_prob_0.2_rms
ds <- ds %>%
select(-pred_prob_0.2_rms)

New function with warning:

#fixed function
pred_fun_final <- function(data, time_to_sur, model) {

newName <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
pred_data <- data[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()

data <- data %>%
mutate({{newName}} := rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv)

data
}
ds <- pred_fun_final(ds, time_to_sur = 0.2, fit)
# Warning message:
# Problem with `mutate()` column `pred_prob_0.2_rms`.
# i `pred_prob_0.2_rms = ...$NULL`.

#save variable
test2 <- ds$pred_prob_0.2_rms

The two variables are not identical but that is because one is named and the other is not (as.numeric() would fix this). It doesn't explain the warning message though.

identical(test1, test2)
#FALSE
str(test1)
# num [1:200] 0.906 0.9 0.884 0.884 0.886 ...
str(test2)
# Named num [1:200] 0.906 0.9 0.884 0.884 0.886 ...
# - attr(*, "names")= chr [1:200] "1" "2" "3" "4" ...

Unable to retrieve variable assigned in function

You must print answer:

  1. inside of ask function,
    or
  2. return answer, catch it and then print it


answer = " "   # set empty in the start
def ask(question):
answer = input(question)
return answer

answer = ask("how are you ")
print(answer)

or one line:

print(ask("how are you "))

Array changes it's values inside a function but other variable don't

This function declaration

void func(int arr[5], int n1, int n2);

is adjusted by the compiler to the declaration

void func(int *arr, int n1, int n2);

That is parameters having array types are adjusted by the compiler to pointers to array element types.

On the other hand, this call

 func(arr,a,b);

is equivalent to the call

 func( &arr[0], a, b );

That is the array designator arr used as the function argument is implicitly converted to a pointer to its first element.

So in fact elements of the array are passed to the function through a pointer to them because using the pointer arithmetic and dereferencing pointer expressions the function has a direct access to elements of the array. For example these expressions

arr[0]++, arr[3]++;

by the definition are equivalent to

( *( arr + 0 ) )++, ( *( arr + 3 ) )++;

As for the variables a and b then the function deals with copies of the values of these variables. If you want to get the same effect as with elements of the array you should define the function like

void func(int arr[5], int *n1, int *n2)
{
cout << "INSIDE func()" << endl;
arr[0]++, arr[3]++;
for(int i = 0; i < 5; i++)
{
cout << arr[i] << ' ';
}
cout << endl;
( *n1 )++, ( *n2 )++;
cout << *n1 << ' ' << *n2 << endl;
}

and call it like

 func( arr, &a, &b );

Java, passing variables/objects into a function

Both - you get a copy of the object reference (for objects), and a copy of the value for primitives.

So unlike C, you can't pass in a variable reference (for a string for example) and end up with it being repointed to something else. And you can't pass in an int, for example, and change it's value within the method - any changes it to it will only be within the method scope.

e.g:

MyObjectHolder holder = new MyObjectHolder();
holder.setObject(new Object());

//holder reference id = 1
// holder.object reference id = 2

doIt(holder);

public void doIt(MyObjectHolder methodScopeHolder) {

// methodScpeHolder reference id = 3
// methodScopeHolder.object reference id = 2

}


Related Topics



Leave a reply



Submit