1

In your opinion what would be the best way to replace something in a string without using $R? I've written a global and I'm trying to replace PETER(s) with PAUL, but not use $R. Here's what iteration of what I thought would work, but it just replaces the first PETER. What would you guys suggest, for multiple Peters on the same line?

Start  
SET ary="^XA"
SET queryary=$QUERY(@ary@(""))
WRITE !,@queryary
FOR   {
SET queryary=$QUERY(@queryary) 
    QUIT:queryary=""  
    w !,$p(@queryary,"PETER",1)_"PAUL"_$p(@queryary,"PETER",2,$l(@queryary,"PETER"))  

}
  QUIT

This is my second try, but I still have to run it multiple times for it to perform all the changes. Is there something missing in my Loop?

  Start  
  N ary
  S ary="^XA"
  S queryary=$Q(@ary@(""))
  S FROM="PETER"
  S TO="PAUL"
  W !,@queryary
  F   S queryary=$Q(@queryary) Q:queryary=""  w !,@queryary   d 
  . f  s $E(@queryary,$F(@queryary,FROM)-$L(FROM),$F(@queryary,FROM))=TO_" "     Q:ary'["PETER"  
  QUIT
M Noob
  • 19
  • 4
  • So thus far, with some doing some reading on $Q and Indirection I have been able to figure out the following – M Noob Mar 28 '16 at 21:20
  • `without using $R`? $R[ANDOM] ? – tsafin Mar 28 '16 at 21:27
  • You had this XNAME global in the question before ) – Evgeny Shvarov Mar 28 '16 at 21:46
  • HAHA yes I did, then I began looking some stuff up, and rewrote everything. Trying to become a programmer and M being my first real language has had some challenges. – M Noob Mar 29 '16 at 12:32
  • Are you required to use ANSI M/Mumps (like Epic programmers have to do), are are you allowed to use the many language enhancements that have been done over the last 20 years in Caché ObjectScript? Personally, I tried to avoid ANSI Mumps like the plague, but COS was a decent language for rapid application development [but I might be biased, having spend 29 years implementing it!] – Scott Jones Apr 16 '16 at 18:03

5 Answers5

2

Without using $replace is tricky. I used the $find and $extract functions... mine replaces "MOZART" with "BACH"

mozartdocument
s ^XA(1)="ONCE UPON A TIME A COMPOSER NAMED MOZART WROTE"
s ^XA(2)="THE 'MOZART PIANO CONCERTO NUMBER ONE'. MOZART"
s ^XA(3)="MOZART 12 MOZART HANDEL MOZART MOZART 12"
s ^XA(4)="MAN MOZART MUMPS MANY MUNCHKINS MOZART"
s ^XA(5)="MOVE ALONG, NOTHING TO SEE HERE!"
s ^XA(6)="123 MOZART 456"
s ^XA(7)="HAPPILY EVER AFTER!"

for z = 1:1:7 {
    do {
        set x = $find(^XA(z),"MOZART")
        set $extract(^XA(z),x-6,x-1)="BACH"
    } while x > 0
    write !,^XA(z)
} write !
1

How about this one?

    ClassMethod PeterPaul()
{
    s ^XNAME(1)="PETER PIPER PICKED A PEPPER"
    s ^XNAME(2)="PETER ENJOYS PIZZA'. PETER" 
    s ^XNAME(3)="PETER WAS BORN IN 1982" 
    s ^XNAME(4)="PETER LIKES PIZZA AND FRENCH FRIES'. PETER" 
    s ^XNAME(5)="THE PETER WROTE A BOOK CALLED PETER ADVENTURES." 
    s ^XNAME(6)="THE PETER HAD THREE KIDS.' PETER JR AND PETER III"
    s ^XNAME(7)="PETER MARRIED MARY."
    s i=$O(^XNAME(""))
    while i'="" {
        s ^XNAME(i)=..Replace(^XNAME(i),"PETER","PAUL")
        s i=$O(^XNAME(i))
        }
    q
}

ClassMethod Replace(str, from, to As %String)
{
        while $F(str,from) {
            s str=$P(str,from)_to_$P(str,from,2,$L(str,from))
            }
        quit str
}
Evgeny Shvarov
  • 468
  • 5
  • 17
1

If you're working in Cache and want a utility for this, %GCHANGE is a very powerful program just for doing what you described. I've always used it as a utility and never called it from a program but I believe there are labels where you an call and pass in your parameters.

The other thing is that you are using multiple indirections in a loop which will slow down your program. I suggest combining all of that into a string and use the E(X)ecute command for indirection on the entire string. You can see the example provided below.

I included two different methods of replacing string. One uses $P and $L similar to what Evgeny Shvarov suggested and the second method is using $F and $E.

The second method on average performed 33% faster on a global of 100000 nodes and 4 replacement per node.

I will include my data gen. and testing functions I wrote as well. I wrote these in legacy MUMPS code so it would work cross platform.

UPDATE: I just checked GTM documentation. %GCE is a similar utility that is avialable in GTM. UPDATE: I Change the REPLACE function to properly account for the LISA to ELISA problem described by C4xuxo. It still performs faster than using $P $L.

UPDATE: Made an adjustment to the value of PS in the REPLACE function to fix a bug;

;GLOBAL REPLACE METHOD 
GLBREPLACE(GLB,STR1,STR2) ;(GLOBAL NAME, STRING TO MATCH, STRING TO REPLACE WITH)
 S CMD="N I S I="""" F  S I=$O("_GLB_"(I)) Q:I=""""  S "_GLB_"(I)=$$REPLACE("_GLB_"(I),"""_STR1_""","""_STR2_""")"
 X CMD Q

;STRING REPLACE METHOD
REPLACE(STR,V1,V2) ;(INPUT STRING, STRING TO MATCH, STRING TO REPLACE WITH)
 N I,L,F1,F2,PS S PS=0,L=$L(STR,V1) F I=1:1:L-1 S F2=$F(STR,V1,PS),F1=F2-$L(V1),$E(STR,F1,F2-1)=V2,PS=F2+$L(V2) 
 Q STR



;======================================================================
;ADDITINAL FUNCTIONS

;THIS IS AN ALTERNATE METHOD, DOESN'T ADDRESS THE LISA TO ELISA PROBLEM
REPLACE2(STR,V1,V2) 
 N I F I=1:1:$L(STR,V1)-1 S STR=$P(STR,V1)_V2_$P(STR,V1,2,$L(STR,V1))
 Q STR

TESTGLBREPLACE ;THIS FUNCTION TESTS GLBREPLACE AND MEASURS PERFORMANCE
 S STIM=$ZTS S COUNT=100000
 D GENDATA(COUNT),GLBREPLACE("^XA","Peter","PAUL")
 S ETIM=$ZTS,TIMDIF=$P(ETIM,",",2)-$P(STIM,",",2),OCCURS=COUNT*4
 W !,"REPLACED "_OCCURS_" OCCURRENCES IN "_TIMDIF_" SECONDS"
 Q

GENDATA(L) ;THIS FUNCTION GENERATES DATA FOR A GIVE COUNT(L=INTEGER)
 F I=1:1:L S ^XA(I)="Peter Piper picked a peck of pickled peppers; A peck of pickled peppers Peter Piper picked; If Peter Piper picked a peck of pickled peppers, Where's the peck of pickled peppers Peter Piper picked"
 Q
Community
  • 1
  • 1
Arya Rasouli
  • 250
  • 4
  • 14
0

Unfortunately I cannot yet post comments, and this was supposed to be more like a comment to previous solution and a question about what is the actual mumps generated by Cache. So if someone reply and confirm my suspicious below would be great, since I think that there is a bug on previous solution.

So assuming the Cache compiles the solution below:

ClassMethod Replace(str, from, to As %String)
{
        while $F(str,from) {
            s str=$P(str,from)_to_$P(str,from,2,$L(str,from))
            }
        quit str
}

To something like this:

REPLACE(str,from,to)
        ;
        F I=1:1 Q:'$F(str,from)  D
        .       S str=$P(str,from)_to_$P(str,from,2,$L(str,from))
        Q str

There is a serious bug in this code that will result in a infinite loop when my actual from variable is contained in to,

Change for example "LISA" to "ELISA", "ELISABETH", "ALISA", "MELISA".

Example used below change DAN to DANIEL.

Tested on GTM (Loop manually interrupted after 10 iteration otherwise would be infinite):

GTM>W $$REPLACE^ZZTEST("DAN SMITH","DAN","DANIEL")
DANIELIELIELIELIELIELIELIELIELIEL SMITH

With this in mind I propose something like:

REPLACE2(str,from,to)
        ;
        N str2
        S str2=""
        F I=1:1:$L(str,from)-1 D
        .       S str2=str2_$P(str,from)_to
        .       S str=$P(str,from,2,$L(str,from))
        ;add the last piece if it exists or in case nothing to replace add all.
        Q str2_str

Tested in GTM:

GTM>W $$REPLACE2^ZZTEST("DAN SMITH","DAN","DANIEL")
DANIEL SMITH
GTM>W $$REPLACE2^ZZTEST("DAN SMITH DAN","DAN","DANIEL")
DANIEL SMITH DANIEL
GTM>W $$REPLACE2^ZZTEST("DAN SMITH DAN DAN DAN","DAN","DANIEL")
DANIEL SMITH DANIEL DANIEL DANIEL
GTM>W $$REPLACE2^ZZTEST("DAN SMITH DAN DAN DAN","DANA","DANIEL")
DAN SMITH DAN DAN DAN

Of course that will not be a final solution since it still contains bugs for example the name LISABETH is generated....

GTM>W $$REPLACE2^ZZTEST("ELISABETH SMITH","ELISA","LISA")
LISABETH SMITH
GTM>W $$REPLACE2^ZZTEST("ELISA ELISABETH SMITH ELISA","ELISA","LISA")
LISA LISABETH SMITH LISA
GTM>W $$REPLACE2^ZZTEST("ELISA ELISABETH SMITH ELISA"," ELISA","LISA")
ELISALISABETH SMITHLISA
GTM>W $$REPLACE2^ZZTEST("ELISA ELISABETH SMITH ELISA"," ELISA ","LISA")
ELISA ELISABETH SMITH ELISA
GTM>W $$REPLACE2^ZZTEST("ELISA ELISABETH SMITH ELISA"," ELISA","LISA")
ELISALISABETH SMITHLISA
GTM>W $$REPLACE2^ZZTEST("ELISA ELISABETH SMITH ELISA","ELISA ","LISA")
LISAELISABETH SMITH ELISA

To bypass this problem additional logic needs to be added to understand that if the name is in the beginning needs to be "NAME " if is at end " NAME", otherwise in the middle " NAME ".

something like (that can probably be optimized):

REPLACE2(str,from,to)
        ;
        N from2,str2
        S str2=""
        S from2=" "_from_" "
        ; check if string begins with name
        I $E(str,1,$L(from))_" "=(from_" ") S str2=to,str=$E(str,$L(from)+1,$L(str))
        ; search for name with spaces
        F I=1:1:$L(str,from2)-1 D
        .       S str2=str2_$P(str,from2)_" "_to
        .       S str=" "_$P(str,from2,2,$L(str,from2))
        ; check if finishes with name
        I $L(str)>=$L(from) D
        .       I $E(str,$L(str)-$L(from),$L(str))=(" "_from) S str2=str2_$E(str,1,$L(str)-$L(from))_to,str=""
        .
        Q str2_str      ;add the last piece if it exists

Test on GTM:

GTM>W $$REPLACE2^ZZTEST("MELISA ELISA ELISABETH ALISA ELISA","ELISA","LISA")
MELISA LISA ELISABETH ALISA LISA
GTM>W $$REPLACE2^ZZTEST("MELISA ELISA ELISABETH ALISA ELISA","LISA","ELISA")
MELISA ELISA ELISABETH ALISA ELISA
GTM>W $$REPLACE2^ZZTEST("LISA MELISA ELISA ELISABETH LISA  ALISA LISA","LISA","ELISA)
ELISA MELISA ELISA ELISABETH ELISA  ALISA ELISA
GTM>W $$REPLACE2^ZZTEST("LISA MELISA ELISA ELISABETH LISA ALISA LISA","LISA","ELISA)
ELISA MELISA ELISA ELISABETH ELISA ALISA ELISA

But still may not attend to all of your needs if you decide or receive input like:

GTM>W $$REPLACE2^ZZTEST("ELISA,SMITH","ELISA","LISA")
ELISA,SMITH
C4xuxo
  • 18
  • 4
0

The standard REPLACE quoted in the Mumps Development Committee minutes is in $$REPLACE^XLFSTR(). I use it very frequently as a printf emulator.


REPLACE(IN,SPEC) ;See $$REPLACE in MDC minutes.
         Q:'$D(IN) "" Q:$D(SPEC)'>9 IN N %1,%2,%3,%4,%5,%6,%7,%8
         S %1=$L(IN),%7=$J("",%1),%3="",%6=9999 F  S %3=$O(SPEC(%3)) Q:%3=""  S %6(%6)=%3,%6=%6-1
         F %6=0:0 S %6=$O(%6(%6)) Q:%6'>0  S %3=%6(%6) D:$D(SPEC(%3))#2 RE1
         S %8="" F %2=1:1:%1 D RE3
         Q %8
         ;
RE1      S %4=$L(%3),%5=0 F  S %5=$F(IN,%3,%5) Q:%5

Here's the reference on how to use it:

http://hardhats.org/kernel/html/x-replace%5Exlfstr.shtml.

Sam Habiel
  • 517
  • 3
  • 9
  • The entire code isn't shown unfortunately. See https://github.com/OSEHRA/VistA-M/blob/master/Packages/Kernel/Routines/XLFSTR.m. – Sam Habiel Mar 29 '16 at 20:56
  • Unless somebody is required to use ANSI Standard M/MUMPS, I'd avoid even showing that sort of 40+ year old code, it's definitely something that leads to people avoiding the language, even though better alternatives are available. Even if you do have to use ANSI Standard M/MUMPS (either because you have to use GT/M or your employer [like Epic] requires it), that coding style, with single characters instead of spelled out command names, and local variables named %[1-8], with lots of commands on each line, should be avoided. – Scott Jones Apr 16 '16 at 18:12