Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Pass by reference can be faked by using HoldAll or something similar in the definition of a "Function". But can pass by reference be faked for symbols passed as named arguments (Options)?

Let me demonstrate:

This works (when you click on the checkboxes the list changes):

ClearAll@PassByArg;
Attributes[PassByArg] = {HoldFirst};
PassByArg[list_] := CheckboxBar[Dynamic[list], Range@Length@list];
aList = {1, 2, 3, 4, 5};
PassByArg[aList]
Dynamic[Sort@aList]

How can I pass the reference to list as an option?

Here is one of my failed attempts (it returns et::setraw: Cannot assign to raw object {1,2,3,4,5} when a checkbox is clicked.

ClearAll@PassByOption;
Options@PassByOption = {"List" -> None};
PassByOption[opts : OptionsPattern[]] := 
  With[{i = ReleaseHold@OptionValue@"List"}, 
   CheckboxBar[Dynamic[i], Range@Length@i]];
aList = {1, 2, 3, 4, 5};
PassByOption["List" -> Hold@aList]
Dynamic[aList]

I always evaluate either too much (to {1,2,3,4,5}) or too little...

Perhaps some motivation is in order: I'm developing a GUI using Dynamic. Now I would like to control a list of displayed functions in some plot. If I implement this as a reference passed with a named argument I practically don't have to change any other code:)

share|improve this question
    
Related: How to modify function argument? –  Mr.Wizard Apr 14 at 20:24
add comment

3 Answers

up vote 8 down vote accepted

This is a case for injector pattern:

PassByOption[opts : OptionsPattern[]] :=
  OptionValue["List"] /. Hold[l_] :> CheckboxBar[Dynamic[l], Range@Length@l]

You can check that this works with this change.

As an alternative, you can by-pass the use of Hold and clever tricks like the above, by using the longer form of OptionValue:

ClearAll@PassByOptionAlt;
Options@PassByOptionAlt = {"List" -> None};
PassByOptionAlt[opts : OptionsPattern[]] :=
  OptionValue[
     PassByOptionAlt, 
     {opts}, 
     "List", 
     Function[l, CheckboxBar[Dynamic[l], Range@Length@l], HoldAll]
  ]

This will work too, but, as mentioned by Szabolcs in comments, you will need to use RuleDelayed (:>) to pass the symbol:

PassByOptionAlt["List" :> aList]
share|improve this answer
    
+1 Thank you very much! –  Ajasja Apr 22 '13 at 22:19
    
@Ajasja Always glad to help :). This question, by the way, should have been asked here long ago, since this is a common situation. –  Leonid Shifrin Apr 22 '13 at 22:26
add comment

Just for fun here is a way to pass the reference as a string (I got this idea from @Leonids answer, but I'm not sure it brings anything useful over the injector pattern):

Here is the code:

ClearAll@PassByOptionStr;
Options@PassByOptionStr={"List"->None};
PassByOptionStr[opts:OptionsPattern[]]:=
    ToExpression[OptionValue["List"],InputForm, 
        Function[l, CheckboxBar[Dynamic[l], Range@Length@l]
    , HoldAll]]

Check that it works:

aList = {1, 2, 3, 4, 5};
PassByOptionStr["List" -> SymbolName@Unevaluated@aList]
Dynamic@aList

This is not directly related to the original question, but the next step is to test if the passed reference is valid. A convenient way to do this is using Leonids valueQ function. Apart from not leaking evaluations valueQ has some nice adavantages over the built in ValueQ. Compare for example:

{valueQ@{1 + 1}, ValueQ@{1 + 1}}

(* ==> {False, True} *)
share|improve this answer
add comment

This seems to do the job :

ClearAll@PassByOption;
Attributes[PassByOption] = {HoldFirst};
PassByOption[opts___] := 
  With[{list = Hold["List"] /. Unevaluated[{opts}], 
        length = Range[Length["List" /. {opts}]]
        }, 
      CheckboxBar[Dynamic[list], length] /. Hold[x_] -> x
      ];
aList = {1, 2, 3, 4, 5};
PassByOption["List" -> aList]
Dynamic[Sort@aList]

Unevaluated is used to prevent aList (from the option "List" -> aList) to be evaluated.

Warning : as Attributes[PassByOption] = {HoldFirst} the option "List" -> aList must be the first option when you call PassByOption

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.