Metaprogramming With Functions

Metaprogramming is a programming technique in which computer programs are treated as data. It means that a program can read, generate, analyze or transform other programs, or even modify itself while running.

DolphinDB supports metaprogramming for dynamic expression generation and delayed evaluation. With metaprogramming, users can generate SQL statements and evaluate them dynamically.

Metacode is objects or expressions within "<" and ">".

Use Metaprogramming in reporting

We can use function format for reporting. It has 2 arguments: a table column and the corresponding format string. The format string follows number and datetime formatting in Java. We can use 6 symbols (0/#/,/./%/E) to represent the format of a number and 9 symbols (y/M/d/H/h/m/s/S/n) to represent the format of a temporal object. For examples:

Input Format Output
2012.12.05T15:30:59.125 yyyy-MM-dd HH:mm 2012-12-05 15:30
2012.12.05 MMMddyyyy DEC052012
0.1356 0.0% 13.6%
1234567.42 #,###.0 1,234,567.4
1234567.42 0.00####E0 1.234567E6

The example below generates a report dynamically given a table, a list of column names, and a list of column formats. Metaprogramming functions sqlCol, sqlColAlias, sql, and makeCall are used to generate a SQL statement and then function eval is called to generate a report. makeCall produces a piece of code that makes a function call. The first parameter is a function and the other parameters are required arguments.

def generateReport(tbl, colNames, colFormat){
   colCount = colNames.size()
   colDefs = array(ANY, colCount)
   for(i in 0:colCount){
      if(colFormat[i] == "")
         colDefs[i] = sqlCol(colNames[i])
      else
         colDefs[i] = sqlCol(colNames[i], format{,colFormat[i]})
   }
   return sql(colDefs, tbl).eval()
}

Generate a sample table with 100 rows and 4 columns (id, date, price, and qty) and then call function generateReport to generate a report.

t = table(1..100 as id, (1..100 + 2018.01.01) as date, rand(100.0, 100) as price, rand(10000, 100) as qty);
t;
id date price qty
1 2018.01.02 61.483376 8733
2 2018.01.03 21.254616 8365
3 2018.01.04 37.408301 444
4 2018.01.05 70.608384 9944
5 2018.01.06 80.361811 7185
... ... ... ...
generateReport(t, ["id", "date","price","qty"], ["0", "MM/dd/yyyy", "0.00", "#,###"]);
id date price qty
1 01/02/2018 61.48 8,733
2 01/03/2018 21.25 8,365
3 01/04/2018 37.41 444
4 01/05/2018 70.61 9,944
5 01/06/2018 80.36 7,185
... ... ... ...

We can use metaprogramming to generate a filtering condition (i.e., where clause in a SQL statement).

def generateReportWithFilter(tbl, colNames, colFormat, filter){
   colCount = colNames.size()
   colDefs = array(ANY, colCount)
   for(i in 0:colCount){
      if(colFormat[i] == "")
         colDefs[i] = sqlCol(colNames[i])
      else
         colDefs[i] = sqlCol(colNames[i], format{,colFormat[i]})
   }
   return sql(colDefs, tbl, filter).eval()
}
generateReportWithFilter(t, ["id","date","price","qty"], ["","MM/dd/yyyy", "00.00", "#,###"], < id>10 and price<20 >);
id date price qty
11 01/12/2018 11.21 7,033
18 01/19/2018 09.97 6,136
29 01/30/2018 06.33 3,834
31 02/01/2018 05.52 6,851
38 02/08/2018 14.55 7,482