I am working through the course Programming Paradigms via Mathematica. One of the exercises asks that you construct a function that mimics Select
,even with three arguments. It also includes the following direction:
Do not use the
Select[]
function in your definition.
Here is an example of how Select
works:
Select[{1, 2, 3, 4, 6, 8, 10}, EvenQ, 4]
(* {2, 4, 6, 8} *)
First I tested the following code and got the same result as Select
:
l1 = {1, 2, 3, 4, 6, 8, 10};
l2 = {};
crit = EvenQ;
n = 4;
While[Length[l1] > 0 && Length[l2] < n,
If[crit[First[l1]], l2 = Append[l2, First[l1]]];
l1 = Rest[l1];
]
l2
(* {2, 4, 6, 8} *)
I then tried to write a function that would do the same thing (caution causes infinite loop):
Clear[whileSelect]
whileSelect[l1_List, crit_, n_] := Module[{l2},
While[Length[l1] > 0 && Length[l2] < n,
If[crit[First[l1]], l2 = Append[l2, First[l1]]];
l1 = Rest[l1];
]
l2
]
However I got a series of Set::shape: Lists {1,2,3,4,6,8,10} and {2,3,4,6,8,10} are not the same shape. >>
error messages and the code put the computer into an infinite loop. I have looked at documentation on Module
,Rest
,Set
and If
but have not been able to figure out what I am doing wrong. I would greatly appreciate a point in the right direction.
l1
in a temporary variable. TrywhileSelect[l1_List, crit_, n_] := Module[{l1temp = l1, l2}, (* stuff *)]
.l1 = Rest[l1]
will not work because if, say, you executedwhileSelect[{1, 2, 3, 4, 6, 8, 10}, EvenQ, 3]
, you then encounter absurdities like{1, 2, 3, 4, 6, 8, 10} = Rest[{1, 2, 3, 4, 6, 8, 10}]
due to replacement. But I'll let someone else flesh out the details... $\endgroup$whileSelect[l1_List, crit_, n_Integer] := Module[{l1temp = l1, l2 = {}, temp}, While[Length[l1temp] > 0 && Length[l2] < n, temp = First[l1temp]; If[crit[temp], AppendTo[l2, temp]]; l1temp = Rest[l1temp]]; l2]
, and note well what I did differently. $\endgroup$Take[Pick[list, fQ /@ list, True], max]
$\endgroup$fQ
) is applied to everylist
element; a second problem occurs ifmax
exceeds the number of matches. Also, there is no need forTrue
inPick
as that is the default. If one chooses not to use built-in functions with a length parameter (e.g.Cases
) I would use something like:select[lst_List, crit_, n_: Infinity] := Module[{f, r = 0}, f = If[r < n, If[crit@#, r++; Sow @ #], Return[]] &; Reap[f ~Scan~ lst][[2, 1]] ]
with a block-based optimization if appropriate. $\endgroup$