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.

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 scape from the block scope. How can I do that?

share|improve this question
    
The third argument of ToExpression is your saviour –  Rojo Feb 18 '13 at 2:14
    
But the idea is to get rid of ToExpression. :) –  Murta Feb 18 '13 at 2:17
    
And what's the practical objective of that idea? –  Rojo Feb 18 '13 at 2:19
    
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. –  Murta Feb 18 '13 at 2:23
1  
If you evaluate Symbol["x"] and x has a value, it will evaluate to the value and not x –  Rojo Feb 18 '13 at 2:27
show 3 more comments

4 Answers 4

up vote 10 down vote accepted

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}
share|improve this answer
    
Nicer than what I drafted in chat, go get the 50k –  Rojo Feb 18 '13 at 2:44
    
@Mr.Wizard You and yours Mathemagics!.. Now I need to study it. Tks! +1 –  Murta Feb 18 '13 at 2:45
add comment

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
share|improve this answer
    
Why not Range@Length@varList? tks +1 –  Murta Feb 18 '13 at 3:09
    
Sure, I didn't know if that was the general intention. Editing –  Rojo Feb 18 '13 at 3:10
add comment

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.

share|improve this answer
    
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! –  Murta Feb 19 '13 at 8:39
    
@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. –  jVincent Feb 20 '13 at 8:11
add comment

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
];
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.