Σάββατο, 3 Νοεμβρίου 2018

FilterMap, FilterFold, Filter, Combine Functions

M2000 has a unique stack system to pass parameters. Each function executed with own stack of values, which erased at the exit. We can pass any number of parameters. Here we use optional arguments. Also we can determine the required type of nth argument from the first or an early reading argument. This happen to FilterMap, where we find if an array has numbers or strings (checking the first item only).
Functions returned type in M2000 can be anything, but we have to use $ in the name to return a string. Interpreter check quickly an expression just before the execution to find if the expression is a string expression, using the $ symbol.

FilterMap return a pointer to array
FilterFold return number, FilterFold$ return string
HasString used as closure to each of the above three functions.

We can combine function to make a bigger filter using Filter() which return function.
We can combine functions for mapping, using combine() and combine$(), one for numeric result and the other for string result. So combine() can be used in FilterFold() and combine$() can be used in FilterFold$()
We can use iterator using Each() but as we see in comments in program, we can't use it direct from expression, but only when we have a named pointer.  This happen because iterator works with a reference to pointer which points to array. This was by design, so an iterator can't work if it is a result from a function, but works fine if passed as an argument, and this happen for FilterFold and FilterMap. When iterator passed by value, it is a pointer, so this is its value. We can pass a pointer to iterator of an array or a pointer to an array. To check if we have an iterator we use Valid(), so for a w we check if Valid(w^) return true. For each iterator, the name of it plus the character "^" is the cursor (or index). So an array has no cursor/index so if a is an array valid(a^) return false. Iterator act as an array (if we read the type we get "mArray", where mArray is the object under the hood). To use iterator we have to place in a When {} structure. We can place a number of iterators, in a list like While A, B, C { } and if any of these end then the While finished. Also we can make more than one iterator for same array and use them both. if w is iterator we can advance to 2nd each time change it inside while using w=each(w, w^+2)
a=(1,2,3,4,5,6,7)
w=each(a)
while w {
      Print array(w)
      w=each(w,w^+2)
}
print
1
3
5
7



Module FilterMapFold {
      Form 80,40
      \\ HasString()
      \\ used to find if an array has strings or numbers
      \\ looking first element
      \\ because a is an iterator of array we have to copy first item
      \\ in a fresh array, which is base 0 by default
      \\ car(a) return first item as an array of one item
      \\ cdr(a) return all others as an array - not used here
      \\ (,) is the empty array - we can use Len() to check this
      HasString=Lambda (&a) ->{
            z=car(a)
            if len(z)=0 then =false :exit
            link z to s()
            =type$(s(0))="String"
      }
      \\ FilterFold$()
      \\ get an array or a pointer to array or an iterator to array
      \\ then optional get filter
      \\ then get the fold function (not optional)
      \\ then get the initial string value - optional
      \\ return string
      FilterFold$=lambda$ HasString (w)-> {
            f=lambda->true
            res$=""
            Read ? f
            Read fold, ? res$
            flush ' empty stack no other arguments allowed
            if not valid(w^) then {m=each(w)} else m=w
            if HasString(&m) then {
                  while m {
                        if not f(array$(m)) then continue
                         Call fold(array$(m), &res$)
                  }
            } else {
                  while m {
                        if not f(array(m)) then continue
                         Call fold(array(m), &res$)
                  }
            }
            =res$
      }
      \\ FilterFold()
      \\ get an array or a pointer to array or an iterator to array
      \\ then optional get filter
      \\ then get the fold function (not optional)
      \\ then get the initial number value - optional
      \\ return number
      FilterFold=lambda HasString (w)-> {
            f=lambda->true
            res=0
            Read ? f
            Read fold, ? res
            flush ' empty stack no other arguments allowed
            if not valid(w^) then {m=each(w)} else m=w
            if HasString(&m) then {
                  while m {
                        if not f(array$(m)) then continue
                         Call fold(array$(m), &res)
                  }
            } else {
                  while m {
                        if not f(array(m)) then continue
                         Call fold(array(m), &res)
                  }      
            }
            =res
      }
      \\ FilterMap()
      \\ get an array or a pointer to array or an iterator to array
      \\ check to see if is an iterator, if not make one
      \\ then optional get filter function
      \\ check if has string or number
      \\ then optional get mapfunction
      \\ return a poinrer to a new array with results
      \\ [ ] get all items from stack and return a stack object
      \\ Array([])  convert stack object to array
      FilterMap=lambda HasString (w)-> {
            if not valid(w^) then {m=each(w)} else m=w
            f=lambda->true
            if HasString(&m) then {
                  map$=lambda$->Letter$
                  Read ? f, map$
                  flush ' empty stack no other arguments allowed
                  while m {
                        if not f(array$(m)) then continue
                        data map$(array$(m))
                  }
            } Else {
                  map=lambda ->Number
                  Read ? f, map
                  flush ' empty stack no other arguments allowed
                  while m {
                        if not f(array(m)) then continue
                        data map(array(m))
                  }
            }
           =Array([])
      }
      \\ we can combine filters using filter()
      \\ we can have any number of lambda functions as parameters
      \\ if any function return false then exit and return falsa
      \\ so return true only if all functions return true
      \\ here we use it with one parameter
      \\ s is a pointer to stack object
      \\ stack(s) is a stack object as copy of s
      \\ ! stack(s)  paste all items to current stack, the lambda stack
      \\ so filter  return a lambda which works for any number and type of arguments
      \\ we use T and F as boolean values - only for print statement
      \\ because True and False are doubles, not boolean, but works nice in boolean expressions
      \\ All comparisons return boolean.
      Function filter {
            Def boolean T=True, F=False
            dim all() : all()=Array([]) : L=len(all())-1
            =lambda all(), L , F, T -> {
                s=[] : =T
                for i=0 to L { if all(i)(!stack(s)) else =F : exit
                }
            }
      }
      \\ example for two parameters
      greater=lambda (x, z)->x>z
      divided=lambda (x, z)->x mod z=0
      myfilter=filter(greater, divided)
      Print myfilter(10,2)=true, myfilter(2,10)=false, myfilter(7,3)=false
     
      \\ combine$()
      \\ take any number of lambda functions, which return string/object result
      \\ combine$() get all parameters to an array and make it  a closure in the returned lambda
      \\ stackitem$() return any type from stack (string or object), without dropping it
      \\ because function's stack always erased at the exit, it make the drop for us.
      Function combine$ {
            dim all$()
            all$()=Array$([])
            L=len(all$())-1
            =lambda$ all$(), L -> {
                for i=0 to L {Push all$(i)(![])} : =StackItem$()
            }
      }
      \\ combine(
      \\ take any number of lambda functions, which return number/object result
      \\ combine() get all parameters to an array and make it  a closure in the returned lambda
      \\ stackitem() return any type from stack (number or object), without dropping it
      \\ because function's stack always erased at the exit, it make the drop for us.
      Function combine {
            dim all()
            all()=Array([])
            L=len(all())-1
            =lambda$ all(), L -> {
                for i=0 to L {Push all(i)(![])} : =StackItem()
            }
      }
      \\ so now we see some example of using these functions
      \\ b is a pointer to array
      b=(1,2,3,4,5,6,7,8)
      \\ just  return a copy of b
      Print FilterMap(b)
      \\ we make a lambda to be used to FilterFold
      \\ second parameter has to be passed by reference
      \\  We can use FilterFolder with String Arrays or Number Arrays
      \\ but we get number  as result (from FilterFolder$ we get string)
      \\ so the reference here must be for a number
      \\ the first parameter here is number because we have number array to fold
      mul=lambda (x, &p) -> {
            p*=x
      }
      \\ using initial value 1  (default is 0, but here 0 isn't good)
      Print FilterFold(b,,mul,1)
      \\ so now we use the same number array but for string result
      \\ we make a text with one to eight starts, like a triangle of stars
      bar$=lambda$ (x, &ret$) ->{
            ret$+=string$("*", x)+{
            }
      }
      \\ Report using 2 center each line, so we get something like a tree
      \\ also report use proportional spacing
      Report 2, FilterFold$(b,,bar$) +"*"
      \\ we can make a new array adding three times b, so now b point to a new array
      b=cons(b,b,b)
      \\ we want the sum of all numbers in b
      Sum=lambda (x, &total)->{
            total+=x
      }
      \\ we leave empty the filter, we place the sum function. Initial value is 0 and this is nice here.
      Print FilterFold(b, ,Sum)
      \\ We want now to get an array of all squares of even numbers in array
      \\ so we want  the Even function as filter (return a boolean)
      \\ and the square function which return squares
      Even=lambda (x)->x mod 2=0
      Square=lambda (x)->x**2
      \\ this is the same
      Square=lambda (x) -> {
            =x**2
      }
      \\ and this is the same too
      Square=lambda -> {
            Read x
            =x**2
      }
      \\ or better , using  Number  which pop a number from lambda's stack
      Square=lambda ->Number**2
      \\ so now we get an array with all values
      Print FilterMap(b, Even, Square)
      \\ We can get the sum too easy:
      Print FilterFold(FilterMap(b, Even, Square), , sum)
      \\ Warning
      \\ Each( )can't work with expression, it need a pointer to array or an array like a()
      \\ so we use c as a pointer to array
      c=FilterMap(Each(b 1 to 8), Even, Square)
      \\ we can see items and length
      Print c, len(c)
      \\ so now we can use each(c,1,2) to get the two first items
      \\ and using FilterFold we get the sum ot those two items
      Print FilterFold(each(c,1,2), , sum)
      \\ We can use two dimensional arrays, or more (maximum ten dimensions)
      \\ we can set different base (low bound) for each dimension
      \\ Dim is always like a "Dim Preserve" in VB6
      Dim z(1 to 4, 1 to 2)
      z(1,1)=1,2,3,4,5,6,7,8
      \\ So now we pass z() to FilterFold, and this check that it has numbers
      \\ and apply the proper code to support the sum function
      Print FilterFold(z(), , sum)
      \\ no it has numbers
      Print HasString(&Z())
      \\ so now we see examples with strings in array
      a=("car","boat","cat","frog")
      \\ check that HasString() works
      Print HasString(&a) ' true
      \\ filters
      \\ check if a$ has a "t" upper or lower case
      HasAt=lambda (a$)->instr(lcase$(a$),"t")>0
      \\ check if a$ has three characters length
      IsThreeLetters=lambda (a$)->len(a$)=3
      \\ maps
      \\ convert to uppercase
      capitalize$=lambda$ (a$)->Ucase$(a$)
      \\ add "123"
      add123$=lambda$ (a$)->a$+"123"
      \\ add brackets
      addbrackets$=lambda$ (a$)->"["+a$+"]"
      \\ Using filterMap with no filter/map, so we get the first two items by each()
      Print filterMap(each(a,1,2))
      \\ now we get all items capitalize
      Print filterMap(a,,capitalize$)
      \\ now we get items with three letters capitalize
      Print filterMap(a,isThreeletters,capitalize$)
      \\  We pass a composite filter using  filter()
      \\ so now we want items with three letters and  have a "t" inside, and map to capitalize
      Print filterMap(a,filter(isThreeletters,HasAt), capitalize$)
      \\  Here we get all items with three letters an apply combine map of two functions
      \\ last function applied last
      Print filterMap(a,isThreeletters, combine$(capitalize$, add123$))
      \\  Here we get all items with three letters an apply combine map
      \\ last applied the addbrackets so we get [CAR123] [CAT123]
      Print filterMap(a,isThreeletters, combine$(capitalize$, add123$, addbrackets$))
      \\ So now we make a folding function
      \\ using string for items and by reference string for accumulator
      appendstring=lambda (x$, &all$)->{
            all$+=x$
      }
      \\ we get all items in a string without spaces
      Print FilterFold$(a,,appendstring)
      \\ we use each with no coma using "to" and Start and End (1 and -1), in reverse
      \\ so we get the items in a string in reverse order
      \\ reverse, we can use each(a, -1, 1)
      Print FilterFold$(each(a End to Start),,appendstring)
      \ like this
      Print FilterFold$(each(a,-1,1),,appendstring)
      \\ we can apply a filter
      Print FilterFold$(a,isThreeletters,appendstring)
      \\ or we can use the FilterMap() as a parameter for FilterFold$()
      Print FilterFold$(FilterMap(a,isThreeletters, combine$(capitalize$, addbrackets$)),,appendstring)
      \\ Another folding function, to get the total length, so we need number,
      \\ so we use FilterFold and not FilterFold$
      GetLength=lambda (x$, &all)-> {
            all+=len(x$)
      }
      \\ Also we can get the maximum length from items
      GetMaxLength=lambda (x$, &max)-> {
            If len(x$)>max then max=len(x$)
      }
      \\ so now we get the length from all items with three letters
      Print FilterFold(a,isThreeletters,GetLength)=6
      \\ and we get the maximum length from all items
      Print FilterFold(a,,GetMaxLength)=4
}
FilterMapFold