user_guide:howto:scripting

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
user_guide:howto:scripting [2019/01/25 16:02] – ↷ Links adapted because of a move operation oroehriguser_guide:howto:scripting [2021/01/12 15:49] (current) – [Location] gawrilow
Line 13: Line 13:
 ===== Structure ===== ===== Structure =====
  
-A script can contain pretty anything allowed by perl syntax rules.  However, to get access to polymake classes and functions, it needs a preamble: <code>use application "NAME";</code>  It sets a default application for the rest of the enclosing lexical context (that is, normally, up to the next ''use application'' statement or the end of the script file, but may also be just the enclosing block).  The notion of the default application has exactly the same meaning as the [[:general#switching_applications|current application]] for the interactive shell:  Functions and class names defined in or imported into the default application may be used without qualification, while names from other applications must be prefixed by the application name.+A script can contain pretty anything allowed by perl syntax rules.  However, to get access to polymake classes and functions, it needs a preamble: <code>use application "NAME";</code>  It sets a default application for the rest of the enclosing lexical context (that is, normally, up to the next ''use application'' statement or the end of the script file, but may also be just the enclosing block).  The notion of the default application has exactly the same meaning as the [[:shell#switching_applications|current application]] for the interactive shell:  Functions and class names defined in or imported into the default application may be used without qualification, while names from other applications must be prefixed by the application name.
  
 The script code is compiled in the package ''Polymake::User'', the same as the interactive shell expressions are evaluated in.  Thus the scripts can access non-local variables introduced in the shell and vice versa, having run the script once, you can use the variables and subroutines defined in the script.  If you want to define additional packages, please define them as subpackages of ''Polymake::User'' or completely outside ''Polymake::'', to prevent accidental clashes with polymake internal classes. The script code is compiled in the package ''Polymake::User'', the same as the interactive shell expressions are evaluated in.  Thus the scripts can access non-local variables introduced in the shell and vice versa, having run the script once, you can use the variables and subroutines defined in the script.  If you want to define additional packages, please define them as subpackages of ''Polymake::User'' or completely outside ''Polymake::'', to prevent accidental clashes with polymake internal classes.
Line 34: Line 34:
  
   * ''$InstallTop/scripts'' -- standard neutral scripts shipped with polymake   * ''$InstallTop/scripts'' -- standard neutral scripts shipped with polymake
-  * ''@lookup_scripts'' -- additional directories of your choice containing your private scripts.  This list is a [[:general#custom_variables|custom variable]] stored in ''~/.polymake/prefer.pl''.+  * ''@lookup_scripts'' -- additional directories of your choice containing your private scripts.  This list is a [[shell_custom#custom_variables|custom variable]].
   * ''$Extension/scripts'' -- neutral scripts coming from an [[user_guide:extend:extensions|extension]]   * ''$Extension/scripts'' -- neutral scripts coming from an [[user_guide:extend:extensions|extension]]
  
Line 44: Line 44:
 These scripts don't need the preamble ''use application'', it is automatically imposed.  An application-specific script can be executed with the ''script'' command if its application is the current one or is imported by the current application. These scripts don't need the preamble ''use application'', it is automatically imposed.  An application-specific script can be executed with the ''script'' command if its application is the current one or is imported by the current application.
  
-Your collection of scripts will probably grow over the time, some scripts sharing common code parts.  The common code is usually extracted in separate ''.pl'' or ''.pm'' files included with ''require'' statement.  The lookup rules for these files is a bit different: the directories to be searched have to be inserted into the global array ''@INC'' The appropriate ''push'' or ''unshift'' statements can be placed in ''~/.polymake/prefer.pl'' somewhere in the ''package Polymake::User'' section.  We recommend, however, to create a private extension and store the scripts and the included files in subdirectories ''scripts'' and ''perllib'' respectively. In this setting you don't have to manipulate any lookup list.+Your collection of scripts will probably grow over the time, some scripts sharing common code parts.  The common code is usually extracted in separate ''.pl'' or ''.pm'' files included with ''require'' statement.  The lookup rules for these files is a bit different: the directories to be searched have to be inserted into the global array ''@INC'' The appropriate ''push'' or ''unshift'' statements can be placed in your personal startup script ''~/.polymake/init.pl'' We recommend, however, to create a private extension and store the scripts and the included files in subdirectories ''scripts'' and ''perllib'' respectively. In this setting you don't have to manipulate any lookup list.
  
 ===== Extended syntax ===== ===== Extended syntax =====
Line 79: Line 79:
 For example, ''$BB::z'' will be treated as ''$AA::BB::z'', and ''$CC::w'' will refer to ''$AA::BB::CC::w'' from within ''AA::BB'', ''AA::BB::DD'' and any other enclosed package. For example, ''$BB::z'' will be treated as ''$AA::BB::z'', and ''$CC::w'' will refer to ''$AA::BB::CC::w'' from within ''AA::BB'', ''AA::BB::DD'' and any other enclosed package.
  
-Package names themselves can be shortened too, but only in few contexts.  The most important place is static function call, like a constructor call ''new C(1,2,3)'' .  The package name must be spelled completely in ''bless'' statements, in ''@ISA'' assignments, in ''UNIVERSAL::isa()'' checks, and ''ref'' comparisons.  As a partial alternative for the latter two cases there is a new expression ''instanceof CLASSNAME($object)'', delivering a boolean TRUE if ''$object'' is born in package //CLASSNAME// or derived thereof; here the //CLASSNAME// is subject to namespace lookup again. +Package names themselves can be shortened too, but only in few contexts.  The most important place is static function call, like a constructor call ''new C(1,2,3)'' .  The package name must be spelled completely in ''package'' and ''bless'' statements, in ''@ISA'' assignments, in ''UNIVERSAL::isa()'' checks, and ''ref'' comparisons.  As a partial alternative for the latter two cases there is a new expression ''instanceof CLASSNAME($object)'', delivering a boolean TRUE if ''$object'' is born in package //CLASSNAME// or derived thereof; here the //CLASSNAME// is subject to namespace lookup again.
- +
-The ''package'' statement used to switch between packages also requires full package names, but there are convenient shortcuts for switching between related packages: ''_'' denotes the previous package, and ''%%__%%'' denotes the one enclosing it.  This notation should resemble the widely adopted designators of the "current directory" ''.'' and the "parent directory" ''..'' +
- +
-''package AA::BB;'' \\ # this is interpreted in traditional way, as ::AA::BB +
-   +
-''package _::CC;'' \\ # here the package AA::BB::CC starts +
- +
-''package %%__::DD;%%'' \\ # here the package AA::BB::DD starts +
- +
-''package %%__%%;'' \\ # here we've come back to AA::BB+
  
 === Extending lookup scope === === Extending lookup scope ===
Line 95: Line 85:
 Each package compiled from polymake source code possesses its own lookup list.  By default it consists of all enclosing packages, but can be easily extended by arbitrary further packages.  This is accomplished with a statement <code>using namespaces "OTHER_PACKAGE", ...;</code> put somewhere in the package being extended.  Please note that this is a real function call, not a pragma, therefore it must be executed early enough to have effect on the package code.  The extension relation is transitive. Each package compiled from polymake source code possesses its own lookup list.  By default it consists of all enclosing packages, but can be easily extended by arbitrary further packages.  This is accomplished with a statement <code>using namespaces "OTHER_PACKAGE", ...;</code> put somewhere in the package being extended.  Please note that this is a real function call, not a pragma, therefore it must be executed early enough to have effect on the package code.  The extension relation is transitive.
  
-This extension mechanism is involved, for example, in the process of importing applications: the application polytope imports everything from applications graph and common, so the lookup list for the package ''Polymake::polytope'' contains ''Polymake:::graph'' and ''Polymake::common'' along with the enclosing ''Polymake'' If some other application would import the application polytope, it would automatically inherit its entire lookup list.+This extension mechanism is involved, for example, in the process of importing applications: the application polytope imports everything from applications graph and common, so the lookup list for the package ''Polymake::polytope'' contains ''Polymake::graph'' and ''Polymake::common'' along with the enclosing ''Polymake'' If some other application would import the application polytope, it would automatically inherit its entire lookup list.
  
 Another form of extending the lookup scope are lexical lookup lists.  They are established using pragma statements <code>use namespaces "OTHER_PACKAGE", ...;</code>  Such a lookup list pertains to the lexical scope containing the pragma statement, it effects the code following it up to the end of the scope or the corresponding ''no namespaces ...;'' statement regardless of the package boundaries.  For scripting, you won't probably use this pragma directly, but rather its derivative <code>use application "APPNAME";</code>  Please note the difference between the package-based and lexical lookup lists: if some name has been found using the former, its entry in the current package symbol table is modified as to point to the imported item, so that all other references to this name in the current package are redirected at once; but if the name lookup has been satisfied using the lexical list, only this very reference is redirected, but not other occurrences elsewhere in the code.  This allows, for example, for different applications to define functions with identical names; the user's script can still call them using unqualified names, the correct instance being chosen depending upon which ''use application'' pragma is in effect at each place. Another form of extending the lookup scope are lexical lookup lists.  They are established using pragma statements <code>use namespaces "OTHER_PACKAGE", ...;</code>  Such a lookup list pertains to the lexical scope containing the pragma statement, it effects the code following it up to the end of the scope or the corresponding ''no namespaces ...;'' statement regardless of the package boundaries.  For scripting, you won't probably use this pragma directly, but rather its derivative <code>use application "APPNAME";</code>  Please note the difference between the package-based and lexical lookup lists: if some name has been found using the former, its entry in the current package symbol table is modified as to point to the imported item, so that all other references to this name in the current package are redirected at once; but if the name lookup has been satisfied using the lexical list, only this very reference is redirected, but not other occurrences elsewhere in the code.  This allows, for example, for different applications to define functions with identical names; the user's script can still call them using unqualified names, the correct instance being chosen depending upon which ''use application'' pragma is in effect at each place.
Line 113: Line 103:
 $c=cast<LatticePolytope>(cube(3)); $c=cast<LatticePolytope>(cube(3));
 </code> </code>
- 
-This syntax is resembling the C++ language as close as possible; unfortunately, this comfort was achieved at the price of severe mistreatment of perl parser.  The consequence is that it can't always correctly recognize the end of the argument list.  If you are going to put several expressions  
-involving templates into a single list (for example, when calling a function), you must enclose each expression in an extra pair of parentheses: 
-  $p=intersection( (new Polytope<Rational>(POINTS=>$p1)), (new Polytope<Rational>(POINTS=>$p2)) ); 
  
 ==== Advanced techniques ==== ==== Advanced techniques ====
Line 131: Line 117:
 === Localizations === === Localizations ===
  
-The standard ''local'' operator is very useful as it allows to make exception-safe temporary changes.  polymake enhances this functionality in two aspects: +The standard ''local'' operator is very useful as it allows to make exception-safe temporary changes.  polymake enhances this functionality in several aspects: 
-   -- ''local'' works with package variables only.  Using new functions ''local_scalar'', ''local_array'', ''local_hash'', and ''local_sub'', you can temporarily modify each data type passed by reference too:\\ ''$a=[1,2];''\\ ''local_array($a[3,4]);'' +   -- ''local'' works with package variables only.  Using additional keywords ''scalar'' and ''ref'', you can temporarily modify lexical variables, scalars returned by lvalue functions, and any data items passed by reference: 
-   .. There are further functions doing temporary changes, which are reverted on leaving the block scope:\\ ''local_incr($scalar, number)'' -- add number\\ ''local_shift(\@array)'' -- hide the first element\\ ''local_pop(\@array)'' -- hide the last element \\ ''local_clip_front(\@arrayn)'' -- hide elements at the front so that the element [n] becomes the first one\\ ''local_clip_back(\@array, n)'' -- hide elements at the end so that the element [n] becomes the last one\\ ''local_unshift(\@arraylist)'' -- prepend elements\\ ''local_push(\@arraylist)'' -- append elements \\ ''local_swap(\@array, i1, i2)'' -- exchange two elements with given indices\\ ''%%local_bless($object, "Package" or \%Package::)%%'' -- change the type of an object +     ? ''local scalar $a=2;'' \\ ''local scalar $obj->member=3;'' 
-   -- You can postpone the reverting actions to an outer enclosing block scope, even located in a different subroutine.  You create a special guard object in the outer scope and pass it to other subroutines.  All temporary modifications remain in effect until the guard object is destroyed+     : Temporarily modify lexical variable or a data member returned as lvalue 
-   .. <code> +     ? ''local scalar $b;'' 
-sub func { +     : Save the current value of a scalar so that it's automatically restored when the localization scope is left 
-  my $guard=shift; +     ? ''local scalar ++$c;'' \\ ''local scalar --$d;'' 
-  $guard->begin_locals; +     : Save the current value and increment/decrement temporarily 
-  local $xyz=123; +     ? ''local ref $e=[3,4];'' \\ ''local ref $f={value => 5};'' 
-  $guard->end_locals; +     :: Temporarily replace an array or hash passed by reference. 
-+     .. Note: it's really the array resp. hash which is modified here, the change will be visible through all references pointing to it. 
-declare $xyz=0; +     ? ''local ref $s=sub { ... }'' 
-+     : Temporarily replace the implementation of subroutine passed by reference 
-  my $guard=new Scope; +     ? ''local bless $obj;'' \\ ''%%local bless $obj, "Package";%%'' 
-  func($guard)+     : Temporarily change the type of an object passed by reference 
-  # $xyz is still 123 +   -- Some basic array modifications can be made temporarily, they are automatically undone when the localization scope is left. Arrays can reside in package or local variables or be passed by reference: 
-+     ? ''local unshift @a, ...;'' \\ ''local push @$b...;'' 
-# $xyz reverted to 0 now</code> +     : Temporarily add elements at the beginning resp. at the end of an array 
-   .. All ''local_XXX'' functions described above can be used in the block ''begin_locals'' .. ''end_locals'' too+     ? ''local shift @a;'' \\ ''local pop @$b;'' 
-   .. There is one session-scope guard dwelling in the variable ''$Polymake::Scope'' (aka ''$Scope'' due to namespace lookup rules).  Its lifetime ends with the complete interpretation of the current input line in the interactive shell, or complete execution of the script started in [[#calling|batch mode]].  This guard is used, among others, by [[:general#preferences|prefer_now]] commands, as well as for purging temporary properties from the objects.+     : Return and temporarily hide the first resp. last element 
 +     ? ''local splice @a0, N;'' 
 +     : Return and temporarily hide the first N elements 
 +     ? ''local splice @aN;'' 
 +     : Return and temporarily hide trailing elements starting with position N (can be negative) 
 +     ? ''local swap @a, i1, i2;'' 
 +     : Temporarily exchange two elements with given indices 
 +   -- Localization scope can be extended to the outer enclosing block or bound to the lifetime of a certain scalar
 +     ? ''local if (...) local $x=1} else { local ref $y=[ 2 ]; }'' 
 +     :: Changes made in then/else branches will be reverted when the block enclosing the entire ''if'' is left 
 +     ? ''local with ($scope{ local $x=1; }'' 
 +     :: Changes made within the block will be reverted when the ''$scope'' scalar is destroyed; this can happen at any later point in time, far away from the block or subroutine enclosing this place.  The same scalar can be used repeatedly in the same or different ''with'' blocks; all reverting actions will be accumulated and applied in reverse order It must have an undefined value before first use
 +     .. There is one session-scope guard ''%%$Scope->locals%%'' used, among others, by [[shell_custom#preferences|prefer_now]] commands and for removing temporary properties from big objects. 
 +     .. If you want to introduce temporary changes lasting until the next interactive shell input or the completion of a script started in [[#calling|batch mode]], \\ use ''%%local with($Scope->locals) { ... }%%'' 
 +     .. If you want to introduce a nested localization scope with shorter lifetime within your script, create a temporary Scope object: ''local $Scope=new Scope();''
  
 ===== Most important interfaces ===== ===== Most important interfaces =====
Line 193: Line 193:
      :: like above, but adding (or replacing) properties      :: like above, but adding (or replacing) properties
      ? ''%%$p->copy_permuted(VERTICES => $v)%%''      ? ''%%$p->copy_permuted(VERTICES => $v)%%''
-     :: first finds a permutation mapping the listed properties of the source object $p to the given values (here: ''%%$p->VERTICES%%'' => ''$v''), then applies it to all source properties and stores the results in the new object.  Invariant properties are copied verbatim.  If no suitable permutation can be found, an exception is raised.+     :: first finds a permutation mapping the listed properties of the source object ''$p'' to the given values (here: ''%%$p->VERTICES%%'' => ''$v''), then applies it to all source properties and stores the results in the new object.  Invariant properties are copied verbatim.  If no suitable permutation can be found, an exception is raised.
  
 === Name and Description === === Name and Description ===
Line 524: Line 524:
  
 Running polymake in debugger has one inconvenience you ought to be warned about: the debugger and the interactive shell share the same instance of the ''readline'' input line editor, so that the history buffer is filled by polymake commands only.  You have to retype the debugger commands completely each time you want to repeat them; fortunately, most of them are just one letter short.  And you may not try TAB completion entering debugger commands - you would land in polymake shell routines! Running polymake in debugger has one inconvenience you ought to be warned about: the debugger and the interactive shell share the same instance of the ''readline'' input line editor, so that the history buffer is filled by polymake commands only.  You have to retype the debugger commands completely each time you want to repeat them; fortunately, most of them are just one letter short.  And you may not try TAB completion entering debugger commands - you would land in polymake shell routines!
 +
  • user_guide/howto/scripting.1548432147.txt.gz
  • Last modified: 2019/01/25 16:02
  • by oroehrig