Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Returning output from a program

    Hi, I would like to know how to return output from a program back to the rest of the do-file. I'm working with strings, so I don't think the 'return' command is what I need. I'm trying to use a local macro. Do I need to use a global macro? Here's what I'm trying:

    *Stata v. 11.2

    program define mytest
    display "Currently on `1'"
    local text_rename = subinstr("`1'", ":", "_", .)
    display "Changed to `text_rename'"
    end

    mytest "hello:"
    display "`text_rename'"

    Thanks

  • #2
    Kevin, why not using the r-results? (and you can return many lines that way, not just one)

    Code:
    clear all
    
    program define mytest, rclass
      version 12.0
      display "Currently on `1'"
      local text_rename = subinstr("`1'", ":", "_", .)
      display "Changed to `text_rename'"
      return local result `"`text_rename'"'
    end
    
    mytest "hello:"
    local text_rename `r(result)'
    display "`text_rename'"
    Output

    Code:
    . mytest "hello:"
    Currently on hello:
    Changed to hello_
    
    . local text_rename `r(result)'
    
    . display "`text_rename'"
    hello_
    Best, Sergiy

    Comment


    • #3
      I do not see why return should not work with strings. Anyway, what you probably seek is c_local. The command is not documented (as far as I know), so here is an example

      Code:
      *Stata v. 11.2
      
      program define mytest
      display "Currently on `1'"
      local text_rename = subinstr("`1'", ":", "_", .)
      display "Changed to `text_rename'"
      // here goes the new line
      c_local text_rename `text_rename'
      end
      
      mytest "hello:"
      display "`text_rename'"
      Best
      Daniel

      Comment


      • #4
        Using a local here won't work, because it goes out of scope after your program ends. A global would work, but globals are always somewhat risky because you might clobber a global that was created by some other program and is still in use. Probably your best bet would be something like this:

        program define mytest, rclass
        display "Currently on `1'"
        local text_rename = subinstr("`1'", ":", "_", .)
        display "Changed to `text_rename'"
        return local new_name `"`text_rename'"'
        exit

        end
        mytest "hello:"
        return list
        display `"`r(new_name)'"'

        Italicized material is where my suggestion differs from your original. (Note: the exit command is not necessary, but is good style.)

        Hope this helps.

        Comment


        • #5
          The return command is exactly what you need. See [U] 18 Programming Stata as a start, along with help return and help program.

          Note that you have defined a local macro text_rename in your program. This macro is "local" to your program - when your program ends, it is automatically deleted, and in any case it's only accessible within the program. This is why it's undefined when you run the final display "`text_rename'" outside the scope of mytest.

          Try this instead:
          Code:
          program define mytest, rclass
              return local text_rename = subinstr("`1'", ":", "_", .)
          end
          
          mytest "hello:"
          return list
          display r(text_rename)
          (You don't need the return list in general, but I put it in there to show you how the returned results work)

          Comment


          • #6
            It is perhaps most common to use an r-class program for this, but s-class would have fewer side-effects. See the help for return.

            Code:
            program define mytest, sclass
                 version 11.2
                 display "Currently on `1'"
                 local text_rename = subinstr("`1'", ":", "_", .)
                 display "Changed to `text_rename'"
                 sreturn local newname "`text_rename'"
            end
            
            mytest frog:toad
            Currently on frog:toad
            Changed to frog_toad
            
            sreturn list
            
            macros:
                        s(newname) : "frog_toad"
            There is a non-documented command to do similar things more directly, but a common line is that you are ready to use it when you discover it for yourself, realise what it does and appreciate how dangerous it could be. "Non-documented" means what it says, not "undocumented".

            Comment


            • #7
              OK, thanks for the several good methods. I'll explore each and learn from them.

              Comment


              • #8
                Apologies, I'm returning to this after a delay. A question. Why am I not able to use the "replace" command below?

                Code:
                program define mytest, sclass
                     version 11.2
                     args original_text
                     display "Currently on `original_text'"
                     replace original_text = subinstr("`original_text'", ":", "_", .)
                     display "Changed to `original_text'"
                     sreturn local newname "`original_text'"
                end
                
                mytest frog:toad
                display s(newname)

                error message: no variables defined


                Redefining the local macro works:

                Code:
                program define mytest, sclass
                     version 11.2
                     args original_text
                     display "Currently on `original_text'"
                     local original_text = subinstr("`original_text'", ":", "_", .)
                     display "Changed to `original_text'"
                     sreturn local newname "`original_text'"
                end
                
                mytest frog:toad
                display s(newname)

                Comment


                • #9
                  original_text is a local macro and can only be manipulated with the local command (as you correctly discovered). replace only works with variables that exist. In your case, it appears that no variables exist so it doesn't even try to figure out if original_text is a variable that can be replaced. If you did have variables, replace would look to see if the variable original_text existed and give you an error if it didn't.

                  Comment


                  • #10
                    To clarify, the particular error, "no variables defined", seems to be related to the fact that I didn't have a dataset open. If I open a dataset and run the code below, I get the following error: variable original_text not found

                    Code:
                    program drop mytest
                    program define mytest, sclass
                         version 11.2
                         args original_text
                         display "Currently on `original_text'"
                         replace original_text = subinstr("`original_text'", ":", "_", .)
                         display "Changed to `original_text'"
                         sreturn local newname "`original_text'"
                    end
                    
                    mytest frog:toad
                    display s(newname)

                    And with single quotes around the local macro, I get the following error: variable frog not found

                    Code:
                    program define mytest, sclass
                         version 11.2
                         args original_text
                         display "Currently on `original_text'"
                         replace `original_text' = subinstr("`original_text'", ":", "_", .)
                         display "Changed to `original_text'"
                         sreturn local newname "`original_text'"
                    end
                    
                    mytest frog:toad
                    display s(newname)

                    Comment


                    • #11
                      Here's what I produced with the information. The purpose is to take variable labels or variable values (strings) and put them into file names for graphs that are automatically generated. I'm calling the program strclean (string clean). I imagine that someone must have done this, or there must be an easier way. Any suggestions? At any rate, it was good practice. (At least for now, I'm avoiding using replace and just defining a new local macro for each change.)

                      Code:
                       program define strclean, sclass
                      version 11.2 args original_text display `"Currently on `original_text'"' local slash_fix = subinstr(`"`original_text'"', "/", "_", .) display `"Slash fix: `slash_fix'"' local backslash_fix = subinstr(`"`slash_fix'"', "\", "_", .) display `"Backslash fix: `backslash_fix'"' local colon_fix = subinstr(`"`backslash_fix'"', ":", "_", .) display `"Colon fix: `colon_fix'"' local asterisk_fix = subinstr(`"`colon_fix'"', "*", "_", .) display `"Asterisk fix: `asterisk_fix'"' local question_fix = subinstr(`"`asterisk_fix'"', "?", "_", .) display `"Question mark fix: `question_fix'"' local quote_fix = subinstr(`"`question_fix'"', `"""', "", .) display `"Quote fix: `quote_fix'"' local less_fix = subinstr(`"`quote_fix'"', "<", "_", .) display `"Less than sign fix: `less_fix'"' local greater_fix = subinstr(`"`less_fix'"', ">", "_", .) display `"Greater than sign fix: `greater_fix'"' local pipe_fix = subinstr(`"`greater_fix'"', "|", "_", .) display `"Pipe fix: `pipe_fix'"' sreturn local text_rename = `"`pipe_fix'"'
                      end strclean `"Hello/\:*?"<>|"' display s(text_rename)
                      Last edited by Kevin McCaffrey; 17 Apr 2014, 16:59.

                      Comment


                      • #12
                        I am very unclear on what you are trying to do.

                        Some or all of this may be of help. I am firing at random here, as your different code segments appear to be aiming at quite different things.

                        1. If you have a string variable, then you can replace colons with underscores in one command line. There is no gain in wrapping that in a program.

                        2. Same statement for a local macro. In fact, putting that replacement in a program makes the task more difficult, as your program can't see the local macro without passing it somehow.

                        3. If you want to feed a program a variable name you can do that as a numbered argument, but it's better style to specify that you want to feed it a variable name

                        Code:
                        program mytest
                              syntax varname
                             ...
                        end
                        4. You have to keep straight the difference between the variable name and its contents. If you want to edit a string variable with subinstr(), the first argument will be the variable name, not literal text that is the variable name.

                        Code:
                        program mytest
                              syntax varname
                              replace `varlist' = subinstr(`varlist', ":", "_", .)
                        end
                        Here

                        Code:
                        program mytest
                              syntax varname
                              replace `varlist' = subinstr("`varlist'", ":", "_", .)
                        end
                        is equivalent just to putting the literal text with the variable name inside the variable so named.


                        5. The first numbered argument will be the first word on the command line, where words are separated by spaces, and bound by double quotation marks.

                        If you call a program mytest with argument

                        frog:toad

                        then by default `1' will be the whole argument "frog:toad". Stata doesn't parse that unless you tell it to.




                        Last edited by Nick Cox; 17 Apr 2014, 18:35.

                        Comment


                        • #13
                          One other problem with Kevin McCaffrey's code: he is using version 11.2, where strings were limited to a fairly short length, 244 characters if I remember correctly.

                          So if the original argument to the program is longer than that, the use of the construction

                          local whatever = subinstr("`whatever_previous'", "this", "that", .)

                          will, because of the equal sign, truncate the result to 244 characters.

                          To preserve the full length of the original argument, he needs to use the macro extended function construction:

                          local whatever: subinstr local whatever_previous "this" "that", all

                          which is limited only by the maximum length of a local macro. (And even back in Stata 11.2, that was far greater than 244 characters.)

                          Comment


                          • #14
                            I'm not exactly sure what the issue is here, since you solved the problem by correctly using local to process your macro instead of incorrectly using replace (see my previous post).

                            As to whether there is an easier way, I'm not sure. With variables there are things like sieve() (part of the egenmore package from SSC) that can screen out specific characters in one step. I don't know if there is something comparable for macros. Perhaps you can look at the egenmore code to see if you can improve on what you've already done. The only thing I might suggest is to use a loop (foreach char in ":" "@" ";" ... {...} ) to make it look prettier.

                            Comment


                            • #15
                              You might also want to have a look at strtoname() (introduced in Stata 11).

                              Best
                              Daniel

                              Comment

                              Working...
                              X