Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!uunet!mcsun!hp4nl!star.cs.vu.nl!jos From: jos@cs.vu.nl (Jos Warmer) Newsgroups: comp.lang.eiffel Subject: Re: Passing functions as parameters Message-ID: <5287@star.cs.vu.nl> Date: 5 Feb 90 13:51:32 GMT References: <1832@clyde.concordia.ca> <120012@gore.com> Sender: news@cs.vu.nl Organization: Fac. Wiskunde & Informatica, VU, Amsterdam Lines: 867 In article <120012@gore.com> jacob@gore.com (Jacob Gore) writes: >/ comp.lang.eiffel / marcap@hercule.cs.concordia.ca (PAWLOWSKY) / Feb 2, 1990 / >> A while back, the passing of functions as parameters was mentioned. If >> memory is correct (I don't have a list of comp.lang.eiffel postings, is >> someone out there keeping a ftp'able copy?) it ended up with someone >> saying it is not needed since you can redefine the function as needed >> in the class. > >I take this as confirmation that no, you can't pass functions as parameters >in Eiffel. > >Could somebody tell me then what the inheritance-related trick is for >supplying a list class with the following routines: > > 1. apply-routine -- like LISP's "mapcar": same operation repeatedly > applied to all elements of the list > This is the `pre_order' or `post_order' from MY_TREE. See later on. > 2. insert-function -- like APL's "/": > given a list L such that nb_elements(L)>1 > and a fuction F, > if L.nb_elements > 2 then > return F(L.first, L.rest) > else > return F(L.first, L.second) This one is not in MY_TREE, but the insert-function could be done in analogy with `pre_order' and `bpre_order'. > >The one particularly useful in Eiffel is a combination of 1 and 2: > > 3. for-all -- given a BOOLEAN function F and a list L, > return 'true' if F(L.i_th(k)) returns true > for all 1 <= k <= L.nb_elements > >And, of course, a there-exists function is analogous. Both for-all and there-exists can easily be implemented by `bpre_order' or `bpost_order' from MY_TREE. See later on. In the example, there-exists is shown. By the way, eiffel naming conventions would call `there-exists' `has'. > >Having such features in list classes would make writing assertions about >lists much easier. > >Jacob >-- >Jacob Gore Jacob@Gore.Com boulder!gore!jacob Yes I can tell you what trick I use. My trick is implemented for tree's, but the idea remains the same for sets, list and so on. I desperately needed a way to traverse complete tree's and perform an action on each node. Because this kind of functionality is needed very often in lists, trees, sets and so on, I see the absense as a serious omission in the library. DESCRIPTION OF THE METHOD Class MY_TREE is an adaption of class TWO_WAY_TREE from the library. It adds several routines for traversing the tree and performing actions on each node of the tree. The routine `pre_order' will be taken as an example. The remaining routines are in the class text below. pre_order( a : TREE_ACTION[T] ) is -- Traverses the tree of which `Current' is the root in preorder -- and performs `a.action' on each node. require not a.Void The routine `pre_order' takes as parameter an entity `a' of type TREE_ACTION[T]. TREE_ACTION is a deferred class with one function called `action': deferred class TREE_ACTION[T] export action feature action(t : MY_TREE[T], level : INTEGER) is deferred end; end -- class TREE_ACTION `pre_order' traverses the tree in preorder and performs a.action(node, level) for each node in the tree. Level is the depth of the node in the tree. To use it, define a descendant of class TREE_ACTION and give an implementation of `action'. I will call such a class an action class. For example, a class to count the number of nodes in a tree: class COUNT_ACTION[T] export action, nr_nodes inherit TREE_ACTION[T]; feature nr_nodes : INTEGER; action( t : MY_TREE[T], level : INTEGER ) is do nr_nodes := nr_nodes + 1; end; end; Usage: ... count: COUNT_ACTION[SOME_TYPE]; tree : MY_TREE [SOME_TYPE]; ... is do count.Create; tree.post_order(count); io.putint( count.nr_nodes ); io.new_line; end; Disadvantages: - For each routine you want to apply, you need to write a separate action class. This might result in lots of little classes. Advantages: - You can apply different routines to the elements of a tree. The method with redefining a function within TWO_WAY_TREE only gives you one routine. - All data associated with the routine that is applied to each node can be local to the action class. Remind that in e.g. C you always need global variables for usage by the routine. - If you need to apply complex operations on each node, then the action class can incorporate all needed routines and variables. I use action classes which are over 200 lines long. Advantages 2 and 3 give much better information hiding then in conventional languages. Following is a shar-archive, which contains the classes I use for this kind of traversals, including an example program. I have used them for almost a year, so they should be reliable. By the way, how do we define `production quality' ? Jos Warmer : This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin:/usr/ucb echo Extracting 'README' sed 's/^X//' > 'README' << '+ END-OF-FILE ''README' XREADME : this file XDESCRIPTION : description of the method used, including (dis)advantages X XThe following four classes should reside in a library directory. XThey implement all kinds of traversal for trees. X Xmy_tree.e : adaption of two_way_tree with traversal routines Xbtree_action.e : deferred action's for traversing my_tree. Xtraverse.e : deferred action's for traversing my_tree. Xtree_action.e : deferred action's for traversing my_tree. X XThe following trhee classes show some examples using the library. X Xexample.e : 'main' program Xexists.e : actual btree_action Xprint_tree.e : actual tree_action + END-OF-FILE README chmod 'u=rw,g=rw,o=' 'README' set `wc -c 'README'` count=$1 case $count in 637) :;; *) echo 'Bad character count in ''README' >&2 echo 'Count should be 637' >&2 esac echo Extracting 'DESCRIPTION' sed 's/^X//' > 'DESCRIPTION' << '+ END-OF-FILE ''DESCRIPTION' X X X DESCRIPTION OF THE METHOD X XClass MY_TREE is an adaption of class TWO_WAY_TREE from the library. XIt adds several routines for traversing the tree and performing actions Xon each node of the tree. The routine `pre_order' will be taken as Xan example. The remaining routines are in the class text below. X X pre_order( a : TREE_ACTION[T] ) is X -- Traverses the tree of which `Current' is the root in preorder X -- and performs `a.action' on each node. X require X not a.Void X XThe routine `pre_order' takes as parameter an entity `a' of Xtype TREE_ACTION[T]. XTREE_ACTION is a deferred class with one function called `action': X X deferred class TREE_ACTION[T] export X action X X feature X action(t : MY_TREE[T], level : INTEGER) is deferred end; X X end -- class TREE_ACTION X X`pre_order' traverses the tree in preorder and performs a.action(node, level) Xfor each node in the tree. Level is the depth of the node in the tree. X XTo use it, define a descendant of class TREE_ACTION and give an Ximplementation of `action'. I will call such a class an action class. XFor example, a class to count the number of nodes in a tree: X X class COUNT_ACTION[T] export X action, nr_nodes X X inherit X TREE_ACTION[T]; X X feature X nr_nodes : INTEGER; X X action( t : MY_TREE[T], level : INTEGER ) is X do X nr_nodes := nr_nodes + 1; X end; X end; X XUsage: X ... X count: COUNT_ACTION[SOME_TYPE]; X tree : MY_TREE [SOME_TYPE]; X X ... is X do X count.Create; X tree.post_order(count); X io.putint( count.nr_nodes ); io.new_line; X end; X XDisadvantages: X X- For each routine you want to apply, you need to write a separate X action class. This might result in lots of little classes. X XAdvantages: X X- You can apply different routines to the elements of a tree. X The method with redefining a function within TWO_WAY_TREE only X gives you one routine. X X- All data associated with the routine that is applied to each node X can be local to the action class. X Remind that in e.g. C you always need global variables for usage X by the routine. X X- If you need to apply complex operations on each node, then the X action class can incorporate all needed routines and variables. X I use action classes which are over 200 lines long. X XAdvantages 2 and 3 give much better information hiding then in Xconventional languages. X XI have used them for almost a year, so they should be reliable. X XBy the way, how do we define `production quality' ? X X Jos Warmer + END-OF-FILE DESCRIPTION chmod 'u=rw,g=rw,o=' 'DESCRIPTION' set `wc -c 'DESCRIPTION'` count=$1 case $count in 2488) :;; *) echo 'Bad character count in ''DESCRIPTION' >&2 echo 'Count should be 2488' >&2 esac echo Extracting 'btree_action.e' sed 's/^X//' > 'btree_action.e' << '+ END-OF-FILE ''btree_action.e' X-- Function to apply during tree-traversal. X-- See MY_TREE. X-- X-- To use it, inherit from this class and define an implementation X-- for `action'. X X-- Author : Jos Warmer , e-mail: jos@cs.vu.nl X-- Organisation: Department of Mathematics and Computer Science X-- Vrije Universiteit Amsterdam X-- Version : 5 Februari 1990 X Xdeferred class BTREE_ACTION[T] export X X action X Xfeature X X action(t : MY_TREE[T], level : INTEGER) : BOOLEAN is deferred end; X Xend -- class BTREE_ACTION + END-OF-FILE btree_action.e chmod 'u=rw,g=r,o=' 'btree_action.e' set `wc -c 'btree_action.e'` count=$1 case $count in 506) :;; *) echo 'Bad character count in ''btree_action.e' >&2 echo 'Count should be 506' >&2 esac echo Extracting 'count_action.e' sed 's/^X//' > 'count_action.e' << '+ END-OF-FILE ''count_action.e' X-- count all nodes in a tree X Xclass COUNT_ACTION[T] export X X action, nr_nodes X Xinherit X TREE_ACTION[T]; X Xfeature X nr_nodes : INTEGER; X X action( t : MY_TREE[T], level : INTEGER ) is X do X nr_nodes := nr_nodes + 1; X end; Xend; + END-OF-FILE count_action.e chmod 'u=rw,g=rw,o=' 'count_action.e' set `wc -c 'count_action.e'` count=$1 case $count in 249) :;; *) echo 'Bad character count in ''count_action.e' >&2 echo 'Count should be 249' >&2 esac echo Extracting 'example.e' sed 's/^X//' > 'example.e' << '+ END-OF-FILE ''example.e' Xclass EXAMPLE export X X -- This class is ment to show the usage of the traverse routines in X -- MY_TREE. X Xfeature X tree : MY_TREE[STRING]; X X Create is X local X tree_printer : PRINT_TREE; X count : COUNT_ACTION[STRING]; X do X fill_tree; X X tree_printer.Create("pre_order"); X tree.pre_order(tree_printer); X tree_printer.close; X X tree_printer.Create("post_order"); X tree.post_order(tree_printer); X tree_printer.close; X X count.Create; X tree.post_order(count); X io.putint(count.nr_nodes); io.putstring(" nodes\n"); X if there_exists("four") then X io.putstring("four exists"); io.new_line; X else X io.putstring("four exists not"); io.new_line; X end; X if there_exists("2") then X io.putstring("2 exists"); io.new_line; X else X io.putstring("2 exists not"); io.new_line; X end; X end; X X fill_tree is X -- Put some strings in the `tree'. X local X tmp : MY_TREE[STRING]; X do X tree.Create("root"); X tree.child_put_right("three"); X tree.child_put_right("two"); X tree.child_put_right("one"); X X tree.child_start; X tmp := tree.child; X tmp.child_put_right("c"); X tmp.child_put_right("b"); X tmp.child_put_right("a"); X X tree.child_start; X tree.child_forth; X tree.child_forth; X tmp := tree.child; X tmp.child_put_right("3"); X tmp.child_put_right("2"); X tmp.child_put_right("1"); X end; X X there_exists( s : STRING ) : BOOLEAN is X -- Is string `s' part of the `tree' ? X local X exists : EXISTS [STRING]; X tmp : MY_TREE[STRING]; X do X exists.Create(s); X tmp := tree.bpre_order(exists); X Result := not tmp.Void; X end; X Xend; + END-OF-FILE example.e chmod 'u=rw,g=rw,o=' 'example.e' set `wc -c 'example.e'` count=$1 case $count in 1564) :;; *) echo 'Bad character count in ''example.e' >&2 echo 'Count should be 1564' >&2 esac echo Extracting 'exists.e' sed 's/^X//' > 'exists.e' << '+ END-OF-FILE ''exists.e' Xclass EXISTS [T->COMPARABLE] export X X action X Xinherit X BTREE_ACTION[T] Xfeature X X item : T; X X Create( s : T ) is X do X item := s; X end; X X action( node : MY_TREE[T] ; level : INTEGER ) : BOOLEAN is X do X Result := not (item >= node.item and item <= node.item); X end; X Xend; X X + END-OF-FILE exists.e chmod 'u=rw,g=rw,o=' 'exists.e' set `wc -c 'exists.e'` count=$1 case $count in 295) :;; *) echo 'Bad character count in ''exists.e' >&2 echo 'Count should be 295' >&2 esac echo Extracting 'my_tree.e' sed 's/^X//' > 'my_tree.e' << '+ END-OF-FILE ''my_tree.e' X-- This is a descendant of TWO_WAY_TREE, with routines added for X-- traversing the tree and performing actions on each node. X X-- Author : Jos Warmer , e-mail: jos@cs.vu.nl X-- Organisation: Department of Mathematics and Computer Science X-- Vrije Universiteit Amsterdam X-- Version : 5 Februari 1990 X Xclass MY_TREE[T] export X repeat TWO_WAY_TREE, X X -- various traverse routines for X -- complete tree or until boolean function returns false X X post_order, pre_order, bpost_order, bpre_order, X traverse, search_pre, X X -- internal routines X do_post_order {MY_TREE}, do_pre_order {MY_TREE}, X do_bpost_order {MY_TREE}, do_bpre_order {MY_TREE}, X do_traverse { MY_TREE} X Xinherit X TWO_WAY_TREE [T] X rename X Create as tree_create X redefine X parent, search_child Xfeature X X parent: MY_TREE [T]; -- Used as anchor. X X Create( v : T ) is X -- Just a copy of the TWO_WAY_TREE Create routine. X do X tree_create(v); X end; X X search_pre( a : BTREE_ACTION[T] ) : like parent is X -- Traverses the tree after `Current' and performs `a.action' X -- on each node. X -- Stops when `a.action' returns FALSE and returns the X -- node on which FALSE was returned. X -- Returns Void when `a.action' returns FALSE nowhere. X -- Keeps on traversing at parent. X require X not a.Void X local X x : like parent; X do X from x := right_sibling X until x.Void or not Result.Void X loop X Result := x.bpre_order(a); X x := x.right_sibling; X end; X if Result.Void and not parent.Void then X Result := parent.search_pre(a); X end; X end; X X bpre_order( a : BTREE_ACTION[T] ) : like parent is X -- Traverses the tree of which `Current' is the root in preorder X -- and performs `a.action' on each node. X -- Stops when `a.action' returns FALSE and returns the X -- node on which false was returned. X -- Returns Void when `a.action' returns FALSE nowhere. X require X not a.Void X do X -- extra routine to supply level X Result := do_bpre_order(a, 0); X end; X X do_bpre_order(a : BTREE_ACTION[T],level : INTEGER) : like parent is X -- This is the working routine for bpre_order. X local X tmp : like parent; X do X if not a.action(Current, level) then X Result := Current; X else X from tmp := first_child X until tmp.Void or not Result.Void X loop X Result := tmp.do_bpre_order(a, level+1); X tmp := tmp.right_sibling X end; X end; X end; X X bpost_order( a : BTREE_ACTION[T] ) : like parent is X -- Traverses the tree of which `Current' is the root in postorder X -- and performs `a.action' on each node. X -- Stops when `a.action' returns FALSE and returns the X -- node on which false was returned. X -- Returns Void when `a.action' returns FALSE nowhere. X require X not a.Void X do X -- extra routine to supply level X Result := do_bpost_order(a, 0); X end; X X do_bpost_order(a : BTREE_ACTION[T],level : INTEGER): like parent is X -- This is the working routine for bpost_order. X local X tmp : like parent; X do X from tmp := first_child X until tmp.Void or not Result.Void X loop X Result := tmp.do_bpost_order(a, level+1); X tmp := tmp.right_sibling X end; X X if not a.action(Current, level) then X Result := Current; X end; X end; X X post_order( a : TREE_ACTION[T] ) is X -- Traverses the tree of which `Current' is the root in postorder X -- and performs `a.action' on each node. X require X not a.Void X do X -- extra routine to supply level X do_post_order(a, 0); X end; X X do_post_order( a : TREE_ACTION[T], level : INTEGER ) is X -- This is the working routine for post_order. X local X tmp : like parent; X do X from tmp := first_child X until tmp.Void X loop X tmp.do_post_order(a, level+1); X tmp := tmp.right_sibling X end; X X a.action(Current, level); X end; X X traverse( action : TRAVERSE[T] ) is X -- Traverses the tree of which `Current' is the root. X -- On each node, a.enter is called first, X -- then the children are traversed, X -- and after that a.leave is called. X -- X -- This combines pre- and post-order traversal: X -- If a.enter is empty, this is the same as post_order. X -- If a.leave is empty, this is the same as pre_order. X do X do_traverse(action, 0); X end; X X do_traverse( action : TRAVERSE[T], level : INTEGER ) is X -- This is the working routine for traverse. X local X tmp : like parent; X do X action.enter(Current, level); X X from tmp := first_child X until tmp.Void X loop X tmp.do_traverse(action, level+1); X tmp := tmp.right_sibling X end; X X action.leave(Current, level); X end; X X pre_order( a : TREE_ACTION[T] ) is X -- Traverses the tree of which `Current' is the root in preorder X -- and performs `a.action' on each node. X require X not a.Void X do X -- extra routine to supply level X do_pre_order(a, 0); X end; X X do_pre_order( a : TREE_ACTION[T], level : INTEGER ) is X -- This is the working routine for do_pre_order. X local X tmp : like parent; X do X a.action(Current, level); X X from tmp := first_child X until tmp.Void X loop X tmp.do_pre_order(a, level+1); X tmp := tmp.right_sibling X end; X end; X X search_child (sought: like parent; i: INTEGER) is X -- Move cursor under `i'-th occurence of `sought' if X -- exists among children; go child_offright if none. X -- X -- NOTE: This is the correct version of `search_child'. X -- The version in the library is incorrect. X require X arguments_not_void: not sought.Void X local X j: INTEGER X do X -- child_mark; -- COMMENTED OUT: this was in the library X from X go_offleft X invariant X child_position >= 0 X variant X arity - child_position X until X child_offright or else (j = i) X loop X child_forth; X if (sought = child) then X j := j + 1 X end -- if X end; X -- child_return -- COMMENTED OUT: this was in the library X -- Using `child_mark' at the entrance and X -- `child_return' at the end, will NEVER X -- move the cursor. X end; -- search_child X Xend -- class MY_TREE + END-OF-FILE my_tree.e chmod 'u=rw,g=r,o=' 'my_tree.e' set `wc -c 'my_tree.e'` count=$1 case $count in 5943) :;; *) echo 'Bad character count in ''my_tree.e' >&2 echo 'Count should be 5943' >&2 esac echo Extracting 'print_tree.e' sed 's/^X//' > 'print_tree.e' << '+ END-OF-FILE ''print_tree.e' XClass PRINT_TREE export X X action, close X Xinherit X TREE_ACTION[STRING] Xfeature X X Tablength : STRING is " "; X file : FILE; X X Create( file_name : STRING ) is X do X file.Create(file_name); X file.open_write; X end; X X action( node : MY_TREE[STRING] ; level : INTEGER ) is X -- Print `node', with `level' tabs indentation on `file'. X local X i : INTEGER; X do X from i := 0 X until i = level X loop X file.putstring(Tablength); X i := i + 1; X end; X file.putstring(node.item); file.new_line; X end; X X close is X do X file.close; X end; X Xend; + END-OF-FILE print_tree.e chmod 'u=rw,g=rw,o=' 'print_tree.e' set `wc -c 'print_tree.e'` count=$1 case $count in 583) :;; *) echo 'Bad character count in ''print_tree.e' >&2 echo 'Count should be 583' >&2 esac echo Extracting 'traverse.e' sed 's/^X//' > 'traverse.e' << '+ END-OF-FILE ''traverse.e' X-- Functions to apply during tree-traversal. X-- See MY_TREE. X-- X-- To use it, inherit from this class and define an implementation X-- for `enter' and `leave'. X X-- Author : Jos Warmer , e-mail: jos@cs.vu.nl X-- Organisation: Department of Mathematics and Computer Science X-- Vrije Universiteit Amsterdam X-- Version : 5 Februari 1990 X Xdeferred class TRAVERSE[T] export X X enter, leave X Xfeature X X enter( node : MY_TREE[T]; level : INTEGER ) is X -- Called at entering the node, before the children are X -- visited. X require X not node.Void; X level >= 0; X deferred X end; X X leave( node : MY_TREE[T]; level : INTEGER ) is X -- Called at leaving the node, after the children are X -- visited. X require X not node.Void; X level >= 0; X deferred X end; X Xend; + END-OF-FILE traverse.e chmod 'u=rw,g=r,o=' 'traverse.e' set `wc -c 'traverse.e'` count=$1 case $count in 799) :;; *) echo 'Bad character count in ''traverse.e' >&2 echo 'Count should be 799' >&2 esac echo Extracting 'tree_action.e' sed 's/^X//' > 'tree_action.e' << '+ END-OF-FILE ''tree_action.e' X-- Function to apply during tree-traversal. X-- See MY_TREE. X-- X-- To use it, inherit from this class and define an implementation X-- for `action'. X X-- Author : Jos Warmer , e-mail: jos@cs.vu.nl X-- Organisation: Department of Mathematics and Computer Science X-- Vrije Universiteit Amsterdam X-- Version : 5 Februari 1990 X Xdeferred class TREE_ACTION[T] export X X action X Xfeature X X action(t : MY_TREE[T], level : INTEGER) is deferred end; X Xend -- class TREE_ACTION + END-OF-FILE tree_action.e chmod 'u=rw,g=r,o=' 'tree_action.e' set `wc -c 'tree_action.e'` count=$1 case $count in 494) :;; *) echo 'Bad character count in ''tree_action.e' >&2 echo 'Count should be 494' >&2 esac echo Extracting '.eiffel' sed 's/^X//' > '.eiffel' << '+ END-OF-FILE ''.eiffel' XROOT: example XUNIVERSE: X /usr/local/eiffel/Eiffel/library/support X /usr/local/eiffel/Eiffel/library/structures XEXTERNAL: XNO_ASSERTION_CHECK (N): XPRECONDITIONS (Y): ALL XALL_ASSERTIONS (N): XDEBUG (N): XTRACE (N): XOPTIMIZE (N): XGARBAGE_COLLECTION (Y) XC_PACKAGE (N): XC_EXTERNAL: XMAKE: XVISIBLE (N): + END-OF-FILE .eiffel chmod 'u=rwx,g=rx,o=' '.eiffel' set `wc -c '.eiffel'` count=$1 case $count in 305) :;; *) echo 'Bad character count in ''.eiffel' >&2 echo 'Count should be 305' >&2 esac exit 0 -- Jos Warmer jos@cs.vu.nl ...uunet!mcvax!cs.vu.nl!jos