maximacomputer-algebra-systems

Maxima: Customized takewhile


I could not find any documentation for takewhile in Maxima.So, I tried writing my own which gives both elements and their position.It starts taking from anywhere in list as soon as the criteria is satisfied. I ended up with two functions with subtle difference,

takewhile(x,p):=block([s:1,temp:[],temp1:[],count:1,xx:create_list([x[i],i],i,makelist(i,i,length(x)))],
                    for i in xx do if apply(p,[first(i)])  then temp:cons(i,temp) ,temp:reverse(temp),
                    if(length(temp)>=2 and flatten(temp)#[]) then
                    (while(count<length(temp) and last(temp[s])+1=last(temp[s+1]) ) do
                    (temp1:cons(temp[s],temp1),count:count+1,s:s+1),
                    if(s<=length(temp)) then (temp1:cons(temp[s],temp1)) else print("Exceeded")) else temp1:temp,reverse(temp1))$

Usage :: takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x>3));

OUTPUT :: [[4,5],[5,6],[7,7],[4,8]]

and second as,

takewhile1(x,p):=block([s:1,temp:[],temp1:[],count:1,xx:create_list([x[i],i],i,makelist(i,i,length(x)))],
                for i in xx do (if parse_string(concat(first(i),p))  then temp:cons(i,temp)) ,temp:reverse(temp),
                if(length(temp)>=2 and flatten(temp)#[]) then 
                (while(last(temp[s])+1=last(temp[s+1]) and count<length(temp)) do 
                (temp1:cons(temp[s],temp1),count:count+1,s:s+1),
                if(s<length(temp)) then temp1:cons(temp[s],temp1)) else temp1:temp,reverse(temp1))$

Usage :: takewhile1([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],\<5);

OUTPUT :: [[2,1],[1,2],[2,3],[3,4],[4,5]]

The subtle difference is in terms of using parse_string to create lambda function instead of applying the lambda taken as parameter from function.

Problem :I can do,

takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x^2+3*x>6));

OUTPUT :: [[2,1]]

But i am not getting how I shall achieve it if I am using takewhile1 as it returns,

concat: argument must be an atom; found ^2>5


Solution

  • I think an imperative version would be much more readable

    load("basic");
    takewhile(lst, pr):= block([l: [], c: []],
      for el in reverse(lst) do if pr(el) then push(el, c)
      else (push(c, l), c: []),
      push(c, l),
      delete([], l));
    

    Tests:

    (%i3) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x>10));
    (%o3)                                 []
    (%i4) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x>0));
    (%o4)           [[2, 1, 2, 3, 4, 5, 7, 4, 1, 4, 5, 2, 1, 7, 8]]
    (%i5) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x>3));
    (%o5)                   [[4, 5, 7, 4], [4, 5], [7, 8]]
    (%i6) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x^2+3*x>6));
    (%o6)            [[2], [2, 3, 4, 5, 7, 4], [4, 5, 2], [7, 8]]
    

    Update: I misundertsood your definition of takewhile. Here is my another try

    takewhile(lst, pr):= block([c: [], n: length(lst)], local(pr),
      reverse(catch(for idx thru n do block([el: part(lst, idx)],
            if not pr(el) and not emptyp(c) then throw(c)
            else if pr(el) then push([el, idx], c)),
          c)));
    

    Tests:

    (%i25) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x>10));
    (%o25)                                []
    (%i26) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x>0));
    (%o26) [[2, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [7, 7], [4, 8], 
                      [1, 9], [4, 10], [5, 11], [2, 12], [1, 13], [7, 14], [8, 15]]
    (%i27) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x>3));
    (%o27)                 [[4, 5], [5, 6], [7, 7], [4, 8]]
    (%i28) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x^2+3*x>6));
    (%o28)                             [[2, 1]]
    (%i29) takewhile([2,1,2,3,4,5,7,4,1,4,5,2,1,7,8],lambda([x],x<5));
    (%o29)             [[2, 1], [1, 2], [2, 3], [3, 4], [4, 5]]