0

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

herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
Pankaj Sejwal
  • 1,605
  • 1
  • 18
  • 27

2 Answers2

2

Constructing expressions via concat and evaluating them via parse_string is almost certainly not a good way to solve the problem. My advice is don't bother trying to figure out why takewhile1 doesn't work, it's not worth the trouble.

About built-in functions for this problem, sublist returns elements satisfying a predicate and sublist_indices returns their positions in the list, while join pastes together two lists. So maybe you can write:

take_while (x, p) := join (sublist (x, p), sublist_indices (x, p));
Robert Dodier
  • 16,905
  • 2
  • 31
  • 48
  • thanks for replying, but I don't mean to go to the end of the list if some element doesn't satisfy the criteria, it needs to stop hence picking only those consecutive elements that fit the condition. This is generating all the elements and their positions.Mr Barton suggested me to use `buildq`, I need to check this too. – Pankaj Sejwal Mar 20 '14 at 18:00
  • 1
    @Rorschach Fair enough, but you still should avoid the use of concat / parse_string. – Robert Dodier Mar 20 '14 at 19:27
  • I agree with you completely on your suggestion, I could focus on other issues in the mean time. I was just checking if it is possible to change string to expression in Maxima, so I found it a good place to try. – Pankaj Sejwal Mar 20 '14 at 19:29
1

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]]
slitvinov
  • 5,693
  • 20
  • 31
  • This is also cool but it misses the positions. But its a better approach than I thought to attack the problem as it reveals more structured information if clubbed with relevant positions as well. – Pankaj Sejwal Mar 20 '14 at 19:19