cancel
Showing results for 
Search instead for 
Did you mean: 

simulating class behavior

Komsit_Prakobph
New Contributor
I need some way to simulate class behaviour in q

Something I can call like this
d: .dictBuilder.new[];
d.put[`y;13];
d.put[`h`j`k; ("a";"b";"c")];
res: d.build[];

show res

x| 1

y| 13

h| "a"

j| "b"

k| "c"


So I came up with this idea
1. store class instance in a namespace dict and keyed by instance id. new dict entry will be added every time new[] is called 
For example, dict3 and dict4 are two instances created in memory
show .dictBuilder
max_instance_id | 4
dict3           | ``y!(();13)
dict4           | ``h`j`k!(();"a";"b";"c")

2. use function projection with instance id to simulate method call on instance (note projection with `dict3])
.dictBuilder.put: {[instance_id;k;v] 
$[0 > type k;
.dictBuilder[instance_id],:(enlist k)! enlist v;
.dictBuilder[instance_id],:(k)! v];
};

.dictBuilder.build: {[instance_id; dummy] 
r: .dictBuilder[instance_id];
.dictBuilder: instance_id _ .dictBuilder; //remove instance after it was built
r: ` _ r;        //remove null key
r};                           

3. instance id is generated and bind to instance method at creation
.dictBuilder.new:{
if[not `dictBuilder in key `; .dictBuilder.max_instance_id: -1]; //create new namespace if not exist
instance_id: `$"d",string .dictBuilder.max_instance_id+:1;       //assign new instance id
.dictBuilder[instance_id]: (enlist`)!enlist();                   //create new instance

builder: (enlist`)!enlist();
builder[`instance_id]: instance_id;
builder[`put]: .dictBuilder.put[instance_id];
builder[`build]: .dictBuilder.build[instance_id];
builder};

4. done. you can call it like above code

Does anyone have other solution for this? Are you aware of any problem with this approach?



The reason why I came up with this idea is simply because I was looking for a cleaner way to do this
To create generic dict, this wouldn't work
d: ()!();
d[`x]: 1;
d[`y]: "f"
`type
So I had to do this. 
d: (enlist`)!enlist();
d[`x]: 1;
d[`y]: "f";
` _ d



10 REPLIES 10

pressjonny0
New Contributor
Hi Komsit

What’s the problem you are trying to solve?  I’m not sure if simulating classes makes a lot of sense in kdb+ - might be better to rethink it.  One thing that may work would be to just use a keyed table - so the “class definition” is the table structure, and each row of the table is an instance of the class.  You would have a bit of overhead (as null/empty values would have to be stored) but it would be easier. 

The other approach using dictionaries, if you really don’t like the null value in the key, is just to enlist everything as you put it in e.g. 

q)d:()!()
q)d[`x]:enlist 1                                                                                                                                                                                           
q)d[`y]:enlist "A"                                                                                                                                                                                         
q)d[`z]:enlist "ABC"                                                                                                                                                                                       
q)d                                                                                                                                                                                                        
x| 1    
y| A    
z| "ABC"

Thanks 

Jonny

Hi Jonny,

Yeah, that's probably a bad idea lol
The null dict key is just a small problem that I had, and it seems to be overkill to try to simulate the class  to solve it. And your solution looks good.

What I'm interest in general is, if anyone has tried to create a class-like data structure in q. I imagine it would be helpful if we can use class in q. But maybe I'm not thinking in q way.

Actually Jonny, this doesn't exactly work for me
d[`x]:enlist 1  <-- then I'll get list of 1 instead of 1i

Yes, you have to un-enlist it (use first) when you get it out. 

Or just live with the null key. 

Thanks 

Jonny 

kuentang
New Contributor

Andrey did something similar in the past:

 

http://code.kx.com/wsvn/code/contrib/azholos/oop.q

 

And yes if possible please avoid oop in kdb.

 

Kim

 

Von: personal-kdbplus@googlegroups.com [mailto:personal-kdbplus@googlegroups.com] Im Auftrag von Komsit Prakobphol
Gesendet: Dienstag, 3. November 2015 14:57
An: Kdb+ Personal Developers
Betreff: [personal kdb+] simulating class behavior

 

I need some way to simulate class behaviour in q

 

Something I can call like this

d: .dictBuilder.new[];

d.put[`y;13];

d.put[`h`j`k; ("a";"b";"c")];

res: d.build[];

show res

x| 1

y| 13

h| "a"

j| "b"

k| "c"

 

So I came up with this idea

1. store class instance in a namespace dict and keyed by instance id. new dict entry will be added every time new[] is called 

For example, dict3 and dict4 are two instances created in memory

show .dictBuilder
max_instance_id | 4

dict3           | ``y!(();13)

dict4           | ``h`j`k!(();"a";"b";"c")


2. use function projection with instance id to simulate method call on instance (note projection with `dict3])

.dictBuilder.put: {[instance_id;k;v] 

      $[0 > type k;

            .dictBuilder[instance_id],:(enlist k)! enlist v;

            .dictBuilder[instance_id],:(k)! v];

      };

.dictBuilder.build: {[instance_id; dummy] 

      r: .dictBuilder[instance_id];

      .dictBuilder: instance_id _ .dictBuilder; //remove instance after it was built

      r: ` _ r;        //remove null key

      r};                           

 

3. instance id is generated and bind to instance method at creation

.dictBuilder.new:{

      if[not `dictBuilder in key `; .dictBuilder.max_instance_id: -1]; //create new namespace if not exist

      instance_id: `$"d",string .dictBuilder.max_instance_id+:1;       //assign new instance id

      .dictBuilder[instance_id]: (enlist`)!enlist();                   //create new instance

 

      builder: (enlist`)!enlist();

      builder[`instance_id]: instance_id;

      builder[`put]: .dictBuilder.put[instance_id];

      builder[`build]: .dictBuilder.build[instance_id];

      builder};


4. done. you can call it like above code

 

Does anyone have other solution for this? Are you aware of any problem with this approach?

 

 

 

The reason why I came up with this idea is simply because I was looking for a cleaner way to do this

To create generic dict, this wouldn't work

d: ()!();

d[`x]: 1;

d[`y]: "f"
`type

So I had to do this. 

d: (enlist`)!enlist();

d[`x]: 1;

d[`y]: "f";

` _ d



--
You received this message because you are subscribed to the Google Groups "Kdb+ Personal Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to personal-kdbplus+unsubscribe@googlegroups.com.
To post to this group, send email to personal-kdbplus@googlegroups.com.
Visit this group at http://groups.google.com/group/personal-kdbplus.
For more options, visit https://groups.google.com/d/optout.

Hi Kim,

Thanks for the link! That is interesting.

Komsit

Hi Komsit,

I created two versions of oop for Q just to see how it looks. One is java-like and the second follows lisp CLOS.

The Java like lib allows you to create objects with new, access their state like obj[`fn;arg1;..] or obj[`field] and etc.Obj is a 'general function'  that calls the correct function based on its args. Contructors, inheritance. get/set are supported. GC can delete objects automatically. The class definition looks like:

.oo.class[`cron.job;()]
 ((`..abstract;1);
  (`Name;`none);
  (`sTime;-0wp);(`eTime;0wp);(`interval;0D01);
  (`.cron;`undef);(`Status;`off);(`Lastval;::);
  (`;`cron.job;.oo.setcnstr`.cron`Name);
  (`;`start;{[th] if[`off=th`Status; if[-16=type t:th`sTime; th[`sTime;.z.P+t]]; if[-16=type t:th`eTime; th[`eTime;.z.P+t]]; th[`.cron][`add;th[`Status;`on]]]; th`this});
  (`;`stop;{[th] th[`.cron][`delete;th[`Status;`off]]; th`this});
  (`;`next;{[TH;prv] if[`off=TH`Status; :0np]; $[null prv;min(max(TH`sTime;.z.P);TH`eTime);null TH`interval;0np;prv=TH`eTime;0np;min(.z.P+TH`interval;TH`eTime)]});
  (`;`run;{[th] }));

.oo.class[`cron.job.periodic;`cron.job]
 ((`args;::);(`fn;{[]});
  (`;`cron.job.periodic;.oo.setcnstr`.cron`Name`fn);
  (`;`run;{[th] th[`Lastval;v:.[th`fn;(),th`args;{"Failed with: ",x}]]; v}));

I've found that class definitions look very messy though this can be useful for some libraries when you need to manage individual state a lot.

CLOS lib allows you create generic functions that call specific functions based on types and/or values of its args like fn[`a;10] vs fn[10;`a]. It supports type hierarchy (list -> table) and Java OOP classes.

If you are interested I can add this lib to contrib svn.

WBR, Andrey Kozyrev.


четверг, 5 ноября 2015 г., 15:53:27 UTC+3 пользователь Komsit Prakobphol написал:
Hi Kim,

Thanks for the link! That is interesting.

Komsit

Hi Andrey - cool, are you using this in any production system? yes, please add to svn. I am interested! thanks!!
Komsit

Hi Komsit,


oop.q is the main file, oolib are some examples (messy).

The basic usage:

.oo.class[`myClass;();((`a;10);(`;`myClass;{[th;x] th[`a;x]});(`;`printA;{[TH] -1 "My val: ",string TH`a;}))]

q)a`a
100
q)a`printA
My val: 100
q)a[`a;+;10]
q)a`printA
My val: 110

.oo.class[`myLine;`myClass;((`b;10);(`;`myLine;{[th;x;y] th[`a;x]; th[`b;y]});(`;`printA;{[th] th[`myClass:printA]; -1 "My second val: ",string th`b;});(`;`getPoint;{[th;x] th[`b]+th[`a]*x}))]
q)l:.oo.new[`myLine;10;20]
q)l`printA
My val: 10
My second val: 20
q)l[`getPoint;10]
120

/ gc can collect unreferenced objects
q)a:l:0
q).oo.gc[]
2

th is like a pointer to the object, TH is the underlying dictionary (works faster but you can't call functions).

WBR, Andrey.

Great, Thank you!