BookmarkSubscribeRSS Feed
ScottBass
Rhodochrosite | Level 12

Hi,

SAS 9.2 on Windows.

I'm writing a macro dealing with hash objects, and would like to reuse the hash object variable for multiple hash objects in the same data step.

The doc for the DELETE() method says:

DATA step component objects are deleted automatically at the end of the DATA step. >>>>> If you want to reuse the object reference variable in another hash or hash iterator object constructor, you should delete the hash or hash iterator object by using the DELETE method. <<<<<

Can someone explain what the >>> emphasized <<< text means???  What exactly does the DELETE() method buy me?

Is the "declare hash" statement a compile time or execution time statement?

Below is test code which illustrates my problem:

%macro do_something_with_hash(key,data);

  declare hash h();

  h.defineKey("&key");

  h.defineData("&data");

  h.defineDone();

  rc=h.delete();

  put rc=;

%mend;

data _null_;

  length foo bar 8;

  * this works ;

  %do_something_with_hash(foo,bar)

  * but a second invocation fails ;

  %do_something_with_hash(blah,blech)

run;

Thanks,

Scott


Please post your question as a self-contained data step in the form of "have" (source) and "want" (desired results).
I won't contribute to your post if I can't cut-and-paste your syntactically correct code into SAS.
8 REPLIES 8
data_null__
Jade | Level 19

_NEW_ Operator, Hash or Hash Iterator Object

%macro do_something_with_hash(key,data);

   *object-reference = _NEW_ object(<argument_tag-1: value-1<, ...argument_tag-n: value-n>>);

   h = _new_ hash();

   h.defineKey("&key");

   h.defineData("&data");

   h.defineDone();

   rc=h.delete();

   put rc=;

%mend;

options mprint=1;

data _null_;

   length foo bar blah blech 8;

   declare hash h;

   * this works ;

   %do_something_with_hash(foo,bar)

   * and so does this;

   %do_something_with_hash(blah,blech)

   run;

ScottBass
Rhodochrosite | Level 12

Thanks Mr. Null,

I was hoping the macro would be "self contained", i.e. I wouldn't have to declare hash h outside the macro.  (This actual code where this is used is actually much more complicated).

Regards,

Scott


Please post your question as a self-contained data step in the form of "have" (source) and "want" (desired results).
I won't contribute to your post if I can't cut-and-paste your syntactically correct code into SAS.
data_null__
Jade | Level 19

Of course the answer is simple "create a called flag".  Making sure if is removed without a warning, which you will complain about, is a bit fiddly but still doable. 

I would be interested know more about your "much more complicated" program, as I can't see how deleting and redefining a hash will be very helpful.

%macro do_something_with_hash(key,data);

   %global &sysmacroname;

   %if %superQ(&sysmacroname) eq %then %do;

      declare hash h;

      %let &sysmacroname = called;

      %end;

   %else %do;

      call execute('%nrstr(%sysfunc(ifC(%sysfunc(symexist('||"&sysmacroname"||')),%nrstr(%symdel '||"&sysmacroname"||';),%nrstr(%put NOTE: Already Done;))))');

      %end;

   *object-reference = _NEW_ object(<argument_tag-1: value-1<, ...argument_tag-n: value-n>>);

   h = _new_ hash();

   h.defineKey("&key");

   h.defineData("&data");

   h.defineDone();

   rc=h.delete();

   %mend;

options mprint=1;

data _null_;

   length foo bar blah blech 8;

   * this works ;

   %do_something_with_hash(foo,bar)

   * and so does this;

   %do_something_with_hash(blah,blech)

   %do_something_with_hash(blah,blech)

   %do_something_with_hash(blah,blech)

   run;

I realized that my SYMDEL bit is too complicated.  The SYMDEL should be done ONCE when the called flag is created.  Using NRSTR will delay executioning until after the data step is done.  Plus if the macro is only called once the called flag still needs to be SYMDELed.

%macro do_something_with_hash(key,data);

   %global &sysmacroname;

   %if %superQ(&sysmacroname) eq %then %do;

      declare hash h;

      %let &sysmacroname = called;

      call execute('%nrstr(%symdel '||"&sysmacroname"||';)');

      %end;

   *object-reference = _NEW_ object(<argument_tag-1: value-1<, ...argument_tag-n: value-n>>);

   h = _new_ hash();

   h.defineKey("&key");

   h.defineData("&data");

   h.defineDone();

   rc=h.delete();

   %mend;

options mprint=1;

data _null_;

   length foo bar blah blech 8;

   * this works ;

   %do_something_with_hash(foo,bar)

   * and so does this;

   %do_something_with_hash(blah,blech)

   %do_something_with_hash(blah,blech)

   %do_something_with_hash(blah,blech)

   run;

DLing
Obsidian | Level 7

I must be missing something here.  (Running SAS 9.1.3 on PC).  The sequence for using hash objects is:

declare hash h;          * Tell data step compiler h is a reference to some hash object ;

h = _new_ hash();        * Create an instance of hash object, store its pointer in h ;

rc = h.defineKey('k');   * Define actual hash object structure ;

rc = h.defineData('d');  * Define actual hash object structure ;

rc = h.defineDone();

...

rc = h.add();

rc = h.find();

rc = r.remove();

...

rc = h.delete();         * The memory structures of the hash object is released, h now is an invalid reference ;

/*   You can then define another hash object and use h to refer to it:  */

h = _new_ hash();        * h now again point to an empty hash object; 

rc = h.defineKey('p');   * Define actual hash object structure ;

rc = h.defineData('q');  * Define actual hash object structure ;

rc = h.defineDone();

...

rc = h.add();

rc = h.find();

rc = r.remove();

...

rc = h.delete();         * Release the hash object, h is no longer a valid hash reference;

If you want to have macro independence, you should

- use different names for the hash tables,

- control when to release each hash object

This way the hash objects won't collide with each other.

However, if you want to have just one name for all the hash object, meaning you'll never need to use more than one of them at a time, you should

- declare the hash reference one time, (as above... only a single declare statement)

- use _new_, defineKey(), defineData(), ... to create/instantiate and use the object

- when done, use delete() to remove the object from memory

- use _new_ again to define another hash object, perhaps with a different structure, but can still use h to point to it

- delete() when it's not needed any more

Note that if you use h = _new_ hash() without first using h.delete(), the object h was pointing to is now "pointerless" and you will have no way to refer to it, unless you have another reference pointing to it.  The thing to remember is to separate "objects" from "pointers or references"... these are dynamic objects, not static.

In the macros above, the hash objects are created and destroyed by the macro.  So when the macro finishes, there are no hash objects left for you to use.  No?

ScottBass
Rhodochrosite | Level 12

Hi,

Maybe I'm missing something too...

A trivial example to illustrate:

%macro create_a_variable(value=);

attrib myvar length=8 format=best. label="My Variable";

myvar=&value;

%mend;

data one;

%create_a_variable(value=1);

%create_a_variable(value=2);

%create_a_variable(value=3);

run;

The macro is "self-contained" - all required variables are declared ***within the macro*** via attrib, and I don't have to keep a flag when myvar is "declared".  %create_a_variable executes an attrib statement, which is a compile time option.  Now, I know myvar gets specified three times during compilation, and last setting wins, but I don't get an error when I try to redefine myvar, esp. when the attributes are the same.

So why do I get an error when I try to declare hash h multiple times in the data step?  No, I wouldn't do this in open code, but it just makes life easier (and consistent with other aspects of SAS) if I don't get a compile time error if I execute "declare hash h" multiple times within a generic macro.

Are there technical reasons, or object oriented programming convention, that caused SAS to write the software this way?  Or is it just the way they decided to do it?

Apologies if I'm just being dense...it happens occasionally.

Scott


Please post your question as a self-contained data step in the form of "have" (source) and "want" (desired results).
I won't contribute to your post if I can't cut-and-paste your syntactically correct code into SAS.
DLing
Obsidian | Level 7

You're not being dense, you raised a very good point.  You got me on that one, I don't understand why SAS did it that way.

As you said, multiple attrib statements, last one wins, no compile time messages generated.  But a redundant declare hash that is syntactically identical does generate an error message.

Perhaps ask SAS to "fix" it, or help us understand the situations where multiple declare hash would causes issues.

Message was edited by: DLing to fix grammar.

Ksharp
Super User

Because you have the same Hash object reference,so you can not use it twice ,since they point actually the same Hash Object.

You can use Null 's suggestion to create new Hash Object reference _new_ hash();

Ksharp

DLing
Obsidian | Level 7

With dynamic data structures, the reference or pointer to it, is very distinct from the memory location or object itself.

declare hash H;   is a compile time statement, saying H will contain references to hash objects, but does not create the object.

declare hash H Q;   says H and Q are hash references, does not create objects.

declare hash H();    says H is a reference to hash objects, and creates an empty hash object.

H = _new_ hash();    creates an empty hash object, and stores its address in H.  H now is a reference to the object.  If H previously pointed to another valid hash object, H now no longer points to it, example:

     declare hash H;          H is a pointer to hash objects

     H = _new_ hash();      create an object.  Store its address in H.

     H = _new_ hash();      create another object.  Store its address in H.  The previous object still exist, but H doesn't point to it.  It is orphaned.

Multiple reference to same object:

     declare hash H Q;     H and Q are pointers to hash objects.

     H = _new_ hash();     create an object.  H points to it.

     Q = H;                      Q now also point to the same hash object.

H.delete();     deletes the object H points to.  But H, the pointer itself, is not deleted.  You can use it to point to another hash object.

     declare hash H;

     H = _new_ hash();    create object #1

     H.delete();               delete object #1

     H = _new_ hash();    create object #2

     H.delete();                delete object #2

Ahh, the fun and games with object oriented programming.  Never thought it'd show up in a SAS data step.

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 8 replies
  • 2025 views
  • 3 likes
  • 4 in conversation