Message Boards Message Boards

5 Replies
0 Total Likes
View groups...
Share this post:

Patterns - need help with a function

Posted 11 years ago
I have defined this pattern:
PPerson = Person[Name_String, Phone_String, Age_Real, Address_String]
p = Person["John", "xxx-xxxx", 34., "US"]
MatchQ[p, PPerson]

I'd like to be able to do the following:
Modify[p, {Name -> "George", Age -> 66.}]

and get this result:
Person["George", "xxx-xxxx", 66., "US"]

Question is: HOW?

For some reason this doesn't work:
Person /: Modify[per : PPerson, Elements : {_Rule ..}] := per /. Elements
POSTED BY: Todor Latev
5 Replies
Posted 11 years ago
To avoid indexing, make the code immune to (some) changes in fields, avoid global variables, avoid "magic constants", the classic solution in programming languages is to use "named fields."  So instead of

PPerson = Person[Name_String, Phone_String, Age_Real, Address_String]

you use

PPerson = Person[{{"Name", Name_String}, {"Phone", Phone_String}, {"Age", Age_Real}, {"Address", Address_String}}]

and define update and access functions like

Modify[p, {"Name", "George"}]

to make the changes you want made. You give your symbols the Orderless attribute so it doesn't matter what order the pairs are given in and I think you have satisfied at least all the requirements you have listed thus far, but I expect more shortly.
POSTED BY: Bill Simpson
Posted 11 years ago
I'm not quite sure why this for example works fine:
Person /: SetName[per : PPerson, name_String] := per /. {Name -> name}
In:=SetName[p, "George"]
Out:=Person["George", "xxx-xxxx", 34., "US"]
And this doesn't:
Person /: Modify[per : PPerson, Elements : {_Rule ..}] := per /. Elements
In:=Modify[p, {Name -> "George"}]
Out:=Person["John", "xxx-xxxx", 34., "US"]
POSTED BY: Todor Latev
@Toder If you wrap the SetName operation in Trace you can see what's going on. In the first case, `Name` is a local symbol because you're using it inside a function that has Person[Name_String, ...] as a pattern. So {Name -> "George"} becomes {"John" -> "George"}). In the second scenario `Name` remains a raw symbol.

There's a lot of ways to approach your general goal. If you want to keep the plain indexed approach you can try a function like this:

Modify[per : PPerson, Elements_] := With[{
    locations = Position[PPerson, #][[1, 1]] & /@ (First /@ Elements)},
   per /. Thread[locations -> (Last /@ Elements)]];

Which grabs the indices from the PPerson pattern itself. Though I think using rules (Person[Name -> "Bob", Phone -> "xxx-xxxx", ...]) is nice overall because you can quickly grab data like this:

Name <> " is " <> ToString[Age] <> " years old" /. (List @@ person)
Posted 11 years ago
Thanks Udo, but this is not quite what I'm looking for.

The reason I inculded the pattern is because I'd like to avoid using indexing - as soon as my pattern changes your routine will have to be updated as well.
If this is used in say 20 additional functions, all of them will have to be rewritten

Until recently I was using this approach:

name =1;
phone =2;
ReplacePart[p, {name -> "George", phone -> "222-222.."}]

This is not a very intelligent solution either, as it introduces a lot of global variables and on top of that it creates a lot of confusion if there's another construct for example, which requires a name with some value other than 1.

The solution that I'm looking for should not introduce any global vars and should figure out the part to be updated from the pattern PPerson. This way if/when PPerson changes the routine will not require modification
POSTED BY: Todor Latev
In[7]:= p1 = ReplacePart[p, {1 -> "George", 3 -> 66.}]

Out[7]= Person["George", "xxx-xxxx", 66., "US"]

In[8]:= MatchQ[p1, PPerson]
Out[8]= True
in p there is no part named "Name" because p is an instance, not a definition:
In[9]:= FreeQ[p, "Name"]

Out[9]= True
POSTED BY: Udo Krause
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
or Discard

Group Abstract Group Abstract