--[[ Complex Number Data Directed Module H. Conrad Cunningham, Professor Computer and Information Science University of Mississippi Developed for CSci 658, Software Language Engineering, Fall 2013 1234567890123456789012345678901234567890123456789012345678901234567890 2013-09-21: Began development of module 2013-09-22: Prototyped data-directed module See the module "complexRectangular" for more extenve comments on the overall example. This module uses the simple non-tagged representation modules with tagged data. It dynamically calls the appropriate functions using a dispatch table organized by operation and type. --]] -- Load complex number utilities module local util = require "complexUtilities" -- local definitions for convenience local square = util.square local show_data = util.show_data -- SICP Section 2.4.3: Data-Directed Programming -- Load Data Tagging module local dt = require "dataTagging" -- local definitions for convenience local attach_tag, is_tagged = dt.attach_tag, dt.is_tagged local type_tag, contents = dt.type_tag, dt.contents -- Data-directed programming functions (similar to section 2.4.3) -- Operation Dispatch Table -- Table "optab" is a table indexed by the names of operations in the -- complex number package. Each entry (i.,e., row) of "optab" is -- itself a table indexed by the types of data representations. Each -- entry in an inner table is a function that can carry out the -- operation on the specificed types. Functions "put" and "get" enable -- entries to be written and read. local optab = {} -- Function "put" takes a table of tables "tab2d" and sets -- "tab2d[key1][key2]" to "value". If the first dimension entry is -- nil for "key1", then "put" creates an empty table before doing the -- assignment. local function put(tab2d,key1,key2,value) assert(type(tab2d) == "table", "First argument of put must be a table.") assert(key1,"Second argument (key1) of put must not be nil.") assert(key2, "Third argument (key2) of put must not be nil.") local row = tab2d[key1] or {} if type(row) == "table" then row[key2] = value tab2d[key1] = row return tab2d else error("In put, first dimension entry is not a table. It is: " .. tostring(key1), 2) end end -- Function "get" takes a table of tables "tab2d", "key1" for its -- first dimension and "key2" for its second. If "tab[key1][key2]" is -- defined, "get" returns the value; else "get" returns nil. local function get(tab2d,key1,key2) if type(tab2d) == "table" then local row = tab2d[key1] if type(row) == "table" then return row[key2] end end return nil end -- Selector names local REAL_PART = "real_part" local IMAG_PART = "imag_part" local MAGNITUDE = "magnitude" local ANGLE = "angle" -- Constructor names local MAKE_FROM_REAL_IMAG = "make_from_real_imag" local MAKE_FROM_MAG_ANG = "make_from_mag_ang" -- String conversion function name local TO_STRING = "to_string" -- Type tags (probably should have these in a separate module) local RECTANGULAR_TAG = "rectangular" local POLAR_TAG = "polar" -- Function "install_package" loads the complex number module -- "cn_mod_name" and installs it's selector and constructor operations -- in the operation table "optab" with type tag "ttag". local function install_package(cn_mod_name,optab,ttag) -- only need selectors, constructors, to_string from module local cn = require(cn_mod_name) local function tag(x) return attach_tag(ttag,x) end put(optab, REAL_PART, ttag, cn.real_part) put(optab, IMAG_PART, ttag, cn.imag_part) put(optab, MAGNITUDE, ttag, cn.magnitude) put(optab, ANGLE, ttag, cn.angle ) put(optab, TO_STRING, ttag, cn.to_string) -- not in SICP put(optab, MAKE_FROM_REAL_IMAG, ttag, function(x,y) return tag(cn.make_from_real_imag(x,y)) end ) put(optab, MAKE_FROM_MAG_ANG, ttag, function(x,y) return tag(cn.make_from_mag_ang(x,y)) end ) end -- Function "dispatch_on_type" retrieves the function from -- 2-dimensional table "tab2d" for operation "op" (1st dimension) and -- the type of the argument "arg" (2nd dimension). It then applies -- the function retrieved to "arg". It only dispatches on the type of -- the argument in one-argument operations. The function in SICP, -- Second Edition, is more general. local function dispatch_on_type(tab2d,op,arg) local tag_used = type_tag(arg) local func = get(tab2d,op,tag_used) if func then return func(contents(arg)) else error("No function for this type in dispatch_on_type: " .. tostring(tag_used), 2) end end -- Generic selectors local function real_part(z) return dispatch_on_type(optab,REAL_PART,z) end local function imag_part(z) return dispatch_on_type(optab,IMAG_PART,z) end local function magnitude(z) return dispatch_on_type(optab,MAGNITUDE,z) end local function angle(z) return dispatch_on_type(optab,ANGLE,z) end -- Constructors for complex numbers local function make_from_real_imag(x,y) return get(optab,MAKE_FROM_REAL_IMAG,RECTANGULAR_TAG)(x,y) end local function make_from_mag_ang(r,a) return get(optab,MAKE_FROM_MAG_ANG,POLAR_TAG)(r,a) end -- Generic string conversion function local function to_string(z) return dispatch_on_type(optab,TO_STRING,z) end -- Complex arithmetic (no change from previous section) local function add_complex(z1,z2) return make_from_real_imag( real_part(z1) + real_part(z2), imag_part(z1) + imag_part(z2) ) end local function sub_complex(z1,z2) return make_from_real_imag( real_part(z1) - real_part(z2), imag_part(z1) - imag_part(z2) ) end local function mul_complex(z1,z2) return make_from_mag_ang( magnitude(z1) * magnitude(z2), angle(z1) + angle(z2) ) end local function div_complex(z1,z2) return make_from_mag_ang( magnitude(z1) / magnitude(z2), angle(z1) - angle(z2) ) end -- MODULE EXPORT return { operation_table = optab, install_package = install_package, make_from_real_imag = make_from_real_imag, make_from_mag_ang = make_from_mag_ang, real_part = real_part, imag_part = imag_part, magnitude = magnitude, angle = angle, add_complex = add_complex, sub_complex = sub_complex, mul_complex = mul_complex, div_complex = div_complex, to_string = to_string }