MathGroup Archive 2007

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Call-by-reference from inside a function

  • To: mathgroup at smc.vnet.net
  • Subject: [mg72574] Re: [mg72568] Call-by-reference from inside a function
  • From: Daniel Lichtblau <danl at wolfram.com>
  • Date: Thu, 11 Jan 2007 02:12:17 -0500 (EST)
  • References: <200701091249.HAA21706@smc.vnet.net>

zac wrote:
> Dear Group,
> 
> I'm in need of a function, which is called with one argument, a list.
> The function is to chose one element of the list randomly, AND modify
> the original list by removing the chosen element from it. My problem is
> that I cannot modify the global variable from inside the function. It
> is not a solution to return with a list such as:
> Return[{chosen_element, modified_local_list}], and than make an
> assignment outside the function.
> I'm thinking on some call-by-reference method, but as I've learned so
> far, there is no user-friendly way to do this, just some workarounds
> (which I don't know). Any suggestions?
> 
> Istvan
> 
> example code below:
> 
> RandomChoseElem[list_List] := Module[
>       {pos, elem},
>       pos = Random[Integer, {1, Length[list]}];
>       elem = list[[pos]];
>       (* This is to be solved inside : *)
>       (* global`list = Drop[global`list, {pos}]; *)
>       Return[elem]
>       ];
> 
> set = {1, 2, 3, 4};
> 
> RandomChoseElem[set]

As several responses noted, using HoldFirst or HoldAll will allow you to 
do this.

I wanted to address an issue of efficiency. If you do this often on 
large lists, you incur an O(n) hit each time from using Drop to rewrite 
the list. Instead you can use a "shuffle" approach wherein you prepend 
an element acting as a "counter", and swap the chosen element with the 
counter current position at each step. This will in effect reorder the 
usable part of the list. Assuming this poses no problem for your 
intended usage, the speed improvement will be an order of magnitude in 
complexity in that each invocation will be O(1).

So here is the specific code. We will begin with a list that has a 1 
explicitly prepended as the counter element.

SetAttributes[randomChooseElem, HoldFirst]

randomChooseElem[list_] := Module[
   {pos, n=list[[1]]+1},
   pos = Random[Integer, {n,Length[list]}];
   list[[1]] = n;
   list[[{n,pos}]] = list[[{pos,n}]];
   list[[n]]
   ]

list = Prepend[Range[10^5],1];

In[15]:= Timing[Do[randomChooseElem[list],{10^4}]]
Out[15]= {0.352022, Null}

Let's check the first few elements.

In[16]:= Take[list,10]

Out[16]= {10001, 70005, 35656, 87986, 65014, 81012,
   46618, 24385, 13312, 24467}

We see that teh counter is now at 10^4+1, and the first few chosen were 
70005, 35656, etc.

Let's look at the last several elements.

In[19]:= Take[list,-10]
Out[19]= {99991, 99992, 8775, 99994, 1917, 9029,
   99997, 99998, 99999, 100000}

As expected, most retain their original values but a few (e.g. 999923) 
were among the 10^4 selections we made.


Daniel Lichtblau
Wolfram Research


  • Prev by Date: Re: ListDimension function
  • Next by Date: Re: ListDimension function
  • Previous by thread: Re: Call-by-reference from inside a function
  • Next by thread: Re: Call-by-reference from inside a function