10
$\begingroup$

I need to create a user-defined Block function where the Block variables' values are defined by code. For example, imagine I have:

SetAttributes[myBlock1,HoldAll]
myBlock1[args_]:=Block[{a=1,b=2,c=3,d=4},args]
myBlock1[{a,b,c,d}]

{1, 2, 3, 4}

Now, what I need is something like:

SetAttributes[myBlock2,HoldAll]
varList={"a","b","c","d"};

myBlock2[args_]:=Module[{varArgs},
    varArgs=MapIndexed[ToString@Row[{#1,"=",#2[[1]]}]&,varList];
    ToExpression@ToString@Row[{"Block[",varArgs,",",ToString@args,"]"}]
]

myBlock2[{a,b,c,d}]

{1, 2, 3, 4}

The above function works, but it's very clumsy (using ToExpression) and error-susceptible. I tried something like:

SetAttributes[myBlock3,HoldAll]
varList={"a","b","c","d"};

myBlock3[args_]:=ReleaseHold[Hold@Block[varDef,args]/.varDef:>MapIndexed[(Evaluate@Symbol[#1]=#2[[1]])&,varList]]
myBlock3[{a,b,c,d}]

But without success in the "variables injection." One important point is that a, b, c and d should not escape from the block scope. How can I do that?

$\endgroup$
8
  • $\begingroup$ The third argument of ToExpression is your saviour $\endgroup$
    – Rojo
    Commented Feb 18, 2013 at 2:14
  • $\begingroup$ But the idea is to get rid of ToExpression. :) $\endgroup$
    – Murta
    Commented Feb 18, 2013 at 2:17
  • $\begingroup$ And what's the practical objective of that idea? $\endgroup$
    – Rojo
    Commented Feb 18, 2013 at 2:19
  • $\begingroup$ Idea to get rid of ToExpression? A simple variation like myBlock2[r={1,2,3};r[[b]]] do not work with this solution, but is ok with the first one. $\endgroup$
    – Murta
    Commented Feb 18, 2013 at 2:23
  • 1
    $\begingroup$ If you evaluate Symbol["x"] and x has a value, it will evaluate to the value and not x $\endgroup$
    – Rojo
    Commented Feb 18, 2013 at 2:27

4 Answers 4

13
$\begingroup$

The two standard methods are SlotSequence, and the "injector pattern."
Related question on StackOverflow: How to Block Symbols without evaluating them?

SlotSequence

ClearAll[myBlock]

SetAttributes[myBlock, HoldAll]

varList = {"a", "b", "c", "d"};

myBlock[args_] :=
 Function[Null, Block[{##}, args], HoldAll] @@
  (MapIndexed[Set, Join @@ MakeExpression@varList] /. {x_} :> x)

myBlock[{a, b, c, d}]
{1, 2, 3, 4}

Injector pattern

ClearAll[myBlock]

SetAttributes[myBlock, HoldAll]

varList = {"a", "b", "c", "d"};

myBlock[args_] :=
 (MapIndexed[Set, Join @@ MakeExpression@varList] /. {x_} :> x) /.
   _[sets__] :> Block[{sets}, args]

myBlock[{a, b, c, d}]
{1, 2, 3, 4}
$\endgroup$
2
  • $\begingroup$ Nicer than what I drafted in chat, go get the 50k $\endgroup$
    – Rojo
    Commented Feb 18, 2013 at 2:44
  • 1
    $\begingroup$ @Mr.Wizard You and yours Mathemagics!.. Now I need to study it. Tks! +1 $\endgroup$
    – Murta
    Commented Feb 18, 2013 at 2:45
7
$\begingroup$

Just an alternative, the abuse of Hold pattern

varList = {"a", "b", "c", "d"};

    SetAttributes[myBlock, HoldFirst];

myBlock[args_]:=
   Hold[Block][
      MakeExpression@varList~Hold[Set]~Range@Length@varList // Thread, 
      Hold[args]] // ReleaseHold
$\endgroup$
2
  • $\begingroup$ Why not Range@Length@varList? tks +1 $\endgroup$
    – Murta
    Commented Feb 18, 2013 at 3:09
  • $\begingroup$ Sure, I didn't know if that was the general intention. Editing $\endgroup$
    – Rojo
    Commented Feb 18, 2013 at 3:10
7
$\begingroup$

I'd like to add that I personally prefer to deal with lists of symbols rather than lists of strings that are implied to convert into symbols later in functions. That way I get errors from incorrectly formated strings early rather then getting them buried deeply in an application when something runs Symbol[string] or worse ToExpression[string] and expects a single symbol. Naturally you can't just have a list of symbols, since they might evaluate, but you can just pretend HoldComplete is List for a moment, and in fact most build-in functions (Map, Sort... the list goes on), don't actually require you to pass things with the List head. Anyway to cut a long rant short, here's a function to convert a list of strings to a HoldComplete filled with symbols:

stringsToSymbols[strings_] := 
 With[{ res =       
       strings/.a_String:>ToExpression[a,InputForm,Hold]//HoldComplete@@#/.Hold[a_]:>a& },
 res/;MatchQ[res,HoldComplete[___Symbol]]]

stringsToSymbols::wrdf="Input strings did not convert nicely to symbol list `1` ";
stringlistToSymHold[s_]:=Message[stringsToSymbols::wrdf,s]

So when needing something like your block function I'll just assume the format:

varList=HoldComplete[a,b,c,d]

Now the above works nicely with the methods presented by Mr. Wizard and Rojo, but I'll show another one just to add it, which is one I've used occasionally. What I do is build the expression-structure using HoldComplete and Lists instead of for instance With Block and similar. I then substitute out all the desired heads at once at the end:

 myBlock[args_] := 
   ReplacePart[
   List[MapIndexed[Set, varList] /. {x_} :> x, 
      args], {{0} -> Block, {1, 0} -> List}]

Here I'd note that I would typically take varList as an input adding varList : HoldComplete[__Symbol] to the function definition, just to avoid reliance on globals.

$\endgroup$
2
  • $\begingroup$ Hi @jVincent! The HoldComplete is a nice tip. About convert text into symbols, I don't like it too, but the variables names came from the names of a SQL query columns, and they are text that I needed to convert into local variables. I have no other option. Tks for your answer! $\endgroup$
    – Murta
    Commented Feb 19, 2013 at 8:39
  • $\begingroup$ @Murta That is why I showed a function to convert from strings to a held format. The point is that sometimes you need to have strings converted into symbols, but when you do, it's better to do so once early, rather than just assume everything will always work out nicely and do the conversion in the middle of other functions. $\endgroup$
    – jVincent
    Commented Feb 20, 2013 at 8:11
1
$\begingroup$

Here's another possibility worth mentioning that I copy from Daniel Huber, found here
http://forums.wolfram.com/mathgroup/archive/2010/Nov/msg00217.html.

It lacks the conversion from string to symbol, but it's already in other answers.

SetAttributes[CreateBlock, HoldAll];
CreateBlock[lvals_, rvals_, expr_] :=
Module[{v, myBlock, mySet, vals},
    v = Thread[mySet[lvals, rvals]];
    SetAttributes[myBlock, HoldAll];
    myBlock[vals, expr] /. vals -> v /. mySet -> Set /. myBlock -> Block
];
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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