How can a new type be defined in Maple? For example a type named fst and given as [list of numbers between 0 and 1 inclusive, list of anything].
Further whenever this fst type object is encountered it is printed useing elements of first sublist as subscripts of elements of second sublist.
Thanking in anticipation.
Comments
extending type and print
Types in Maple are fully extensible. Make a procedure `type/fst` which checks your type. For example:
Note that the type listlist checks for a list of lists, where all sublists have the same length.
This won't work for printing however. In Maple you can only define how to print a function. Your solution is to make a new function with two arguments (the two lists), for example FST([0.2, 0.3], [x,y]); The lists will get passed to a printing procedure `print/FST` if it exists.
I hope this gets you started.
Bad style in type
Your
should take full advantage of structured types and should be
`type/FST` := 'FST'([list(And(numeric, satisfies(proc(a) evalb(a >=0 and a <= 1)), list(anything)])This has several advantages. First, it is significantly faster since the overhead of a function call in Maple is so large. Second, it will give better error messages when something goes wrong. Third, the above makes sure that the first list contains numerics before checking for ≥ 0 which can give you infamous "cannot evaluate boolean" otherwise.
Using
is particularly hideous. However showed you this as a good way to do things should read a Maple manual from the early 90s or later... The above is assembler-style Maple, shudder. Structured types are your friends. Get comfortable with them. And then you'll see how powerful
indets,evalindetsandtypematchreally are.not so fast
Interesting comment, however, your suggestion has a few problems. First, it doesn't check that the number of elements in the two lists match. Second, there is a typo (missing terminator for the procedure, no biggie). Third, it is not, in fact, faster than Roman's suggestion, but slower---it's half the speed for lists with 10 elements. I suspect that the cause is the use of 'satisfies'. Using, as you suggest, a structured function to replace the "hideous" line reduces the time by about 20%. A more substantial savings can be made by replacing the checking of each index with calls to min and max. The following is about 3x faster than Roman's original:
AddType('fst3' , proc(f) ( type(f, 'FST'(list(numeric),list)) and nops(op(1,f)) = nops(op(2,f)) and min(op(op(1,f))) >= 0 and max(op(op(1,f))) <= 1 ) end proc);Not knowing what the orignal poster intends, its hard to say whether speed in type checking this data-structure is an issue. If it were, I'd suggest using something simple and fast, say, specfunc(anything,'FST') and doing the validation as a separate step, only when needed. Using the modular approach I suggested, where a new structure is checked at creation, might eliminate the need for re-validation.
TypeTools and Print considerations
Roman's suggestion should get you started, but here are a few finer points to consider. Rather than using `type/FST` to create the type, you might consider using TypeTools[AddType]. Thus
TypeTools[AddType]('FST' , proc(f) ( type(f, specfunc(list,'FST')) and nops(f)=2 and type([op(f)], 'listlist') and andmap(proc(a) evalb(a >= 0 and a <= 1) end proc, op(1,f))); end proc);An advantage to using TypeTools is that the global name space isn't polluted. Alas, there is currently no equivalent PrintTools package, so to handle the printing you need to assign the global procedure `print/FST`.
You specified that anything could be allowed in the second list. If that is indeed the case (though I suspect names might be more likely) you may need to modify Roman's suggested `print/FST` procedure. Consider what would happen if an element of the list were actually a list, say [a]. Then the if the corresponding 'index' is 0.5 the expression [a][0.5] is created by the print procedure, which generates an error. Another possibility is that the index is 1, which creates [a][1] and so prints `a', not what you wanted.
If that is a concern, one possiblity is to do something like
`print/FST` := proc(a, b) local i; if type('FST'(args),'FST') then [seq](convert(b[i],'`local`')[a[i]], i=1..nops(a)); else 'FST'(args) end if; end proc:This converts the elements of the second list to local names and then appends an index to them. The reason for adding the conditional was to handle the case of an improperly formed FST element, say FST(1,2,3).
A modular approach
Another way to handle this is to use a module as the data structure. One advantage to using a module is that a ModulePrint member can be assigned; it is used whenever the module is displayed. Type checking can be simplified because modules are created with a constructor that validates the fields. Here is one approach. I chose the names `index' and `value' for the two lists in the old FST data-structure, the names probably are not appropriate.
fst := module() export New; local ModuleLoad, printfst; option package; printfst := proc(indx, vals) local i; [seq](convert(vals[i],'`local`')[indx[i]], i=1..nops(indx)); end proc; ModuleLoad := proc() TypeTools['AddType'](':-FST' , m -> type(m,'`module`'('index', 'value'))); end proc; New := proc(indx :: list(numeric), vals :: list(name)) if nops(indx) <> nops(vals) then error "number of indices does not match number of values"; elif not andmap( proc(ix) evalb(0 <= ix and ix <= 1) end proc , indx ) then error "indices must be between 0 and 1, inclusive" end if; module() export index, value; local ModulePrint; ModulePrint := () -> printfst(index,value); index := indx; value := vals; end module; end proc; ModuleLoad(); end module:To use this you could do
The above implementation is simplistic. You might not want to export index and value, that allows a user to directly modify them and hence possibly generate an illegal structure. Safer would be to export methods (procedures) that access (and modify) local fields.