Skip to main content

evaluation - Functional-Style Fixed-Length Queue Object?


Following up on question Side-effecting an array in an association? and @Mr.Wizard's answer in there, I'm modernizing and tweaking Daniel Lichtblau's efficient queues and ran into a little roadblock.


First, I came up with the following API, stripped-down for brevity. The queue I want is fixed-length and lossy. Make a new queue with newQ[capacity]. Push values to the back with pushQ[aQueue, aValue]; pop values from the front with popQ[aQueue], just like a queue of humans. If the queue is full, just pop the value at the front, losing it (on purpose)


I want pushQ to return the queue it receives, so that I can fold it and chain it as follows:


Fold[pushQ, newQ[4], Range[10]]

Here's what I came up with, with the functional mojo highlighted:


ClearAll[newQ, emptyQQ, fullQQ, pushQ, popQ, peekQ, bumpQ]; 

newQ[cap_Integer] /; cap > 0 :=
<|"storage" -> ConstantArray[Null, cap],
"len" -> 0, "cap" -> cap,
"iFront" -> 1, "iBack" -> 1|>;
emptyQQ[Q_] := Q[["len"]] === 0;
fullQQ[Q_] := Q[["len"]] === Q[["cap"]];
peekQ[Q_] := Q[["storage", Q[["iFront"]]]];
SetAttributes[bumpQ, HoldFirst];
bumpQ[Q_, index_] := Q[[index]] = Mod[Q[[index]] + 1, Q[["cap"]], 1];
SetAttributes[pushQ, HoldFirst];

pushQ[Q_, item_] :=
(Q[["storage", Q[["iBack"]]]] = item;
bumpQ[Q, "iBack"];
If[fullQQ[Q],
(*then*)bumpQ[Q, "iFront"],
(*else*)Q[["len"]]++];
Q); (* <<<<<<< HERE IS THE FUNCTIONAL MOJO <<<<<<<< *)
SetAttributes[popQ, HoldFirst];
popQ[Q_] /; emptyQQ[Q] := Null;
popQ[Q_] :=

(Q[["len"]]--;
With[ {result = Q[["storage", Q[["iFront"]]]]},
Q[["storage", Q[["iFront"]]]] = Null;
bumpQ[Q, "iFront"];
result ])

This passes a bunch of unit tests:


$q = newQ[4];
pushQ[$q, 1]; $q["storage"]



{1, Null, Null, Null}

pushQ[$q, 2]; $q["storage"]


{1, 2, Null, Null}

pushQ[$q, 3]; $q["storage"]



{1, 2, 3, Null}

popQ[$q]


1

$q["storage"]



{Null, 2, 3, Null}

popQ[$q]; $q["storage"]


{Null, Null, 3, Null}

pushQ[$q, 4]; $q["storage"]



{Null, Null, 3, 4}

pushQ[$q, 5]; $q["storage"]


{5, Null, 3, 4}

popQ[$q]



3

$q["storage"]


{5, Null, Null, 4}

pushQ[$q, 6]; pushQ[$q, 7]; pushQ[$q, 8]; pushQ[$q, 9]; pushQ[$q, 10];
$q["storage"]



{9, 10, 7, 8}

popQ[$q]


7

and so on. Now, when I try the obvious generalization to folding, I'm back where I started with the ... in the part assignment is not a symbol.



Fold[pushQ, newQ[4], Range[1]]


During evaluation of In[49]:= Set::setps: <|storage->{Null,Null,Null,Null},len->0,cap->4,iFront->1,iBack->1|> in the part assignment is not a symbol. >>
During evaluation of In[49]:= Set::setps: <|storage->{Null,Null,Null,Null},len->0,cap->4,iFront->1,iBack->1|> in the part assignment is not a symbol. >>
During evaluation of In[49]:= General::stop: Further output of Set::setps will be suppressed during this calculation. >>
<|"storage" -> {Null, Null, Null, Null}, "len" -> 0, "cap" -> 4, "iFront" -> 1, "iBack" -> 1|>

Ok, I need some kind of symbol for Set to work. A couple more attempts as follows produce the same results, and I did a bunch of tracing and printing, and couldn't see a good way through this:


Module[{$q = newQ[4]}, Fold[pushQ, $q, Range[1]]]



During evaluation of In[48]:= Set::setps: <|storage->{Null,Null,Null,Null},len->0,cap->4,iFront->1,iBack->1|> in the part assignment is not a symbol. >>

Module[{$q = newQ[4]}, Fold[($q = pushQ[#1, #2]) &, $q, Range[1]]]


During evaluation of In[44]:= Set::setps: <|storage->{Null,Null,Null,Null},len->0,cap->4,iFront->1,iBack->1|> in the part assignment is not a symbol. >>

EDIT:



This question is really about elegance because I can get meaningful, useful side effects with Map and Scan, as follows:


Module[{q = newQ[4]}, pushQ[q, #] & /@ Range[10]]


{<|"storage" -> {1, Null, Null, Null}, "len" -> 1, "cap" -> 4, "iFront" -> 1, "iBack" -> 2|>, 
<|"storage" -> {1, 2, Null, Null}, "len" -> 2, "cap" -> 4, "iFront" -> 1, "iBack" -> 3|>,
<|"storage" -> {1, 2, 3, Null}, "len" -> 3, "cap" -> 4, "iFront" -> 1, "iBack" -> 4|>,
<|"storage" -> {1, 2, 3, 4}, "len" -> 4, "cap" -> 4, "iFront" -> 1, "iBack" -> 1|>,
<|"storage" -> {5, 2, 3, 4}, "len" -> 4, "cap" -> 4, "iFront" -> 2, "iBack" -> 2|>,
<|"storage" -> {5, 6, 3, 4}, "len" -> 4, "cap" -> 4, "iFront" -> 3, "iBack" -> 3|>,

<|"storage" -> {5, 6, 7, 4}, "len" -> 4, "cap" -> 4, "iFront" -> 4, "iBack" -> 4|>,
<|"storage" -> {5, 6, 7, 8}, "len" -> 4, "cap" -> 4, "iFront" -> 1, "iBack" -> 1|>,
<|"storage" -> {9, 6, 7, 8}, "len" -> 4, "cap" -> 4, "iFront" -> 2, "iBack" -> 2|>,
<|"storage" -> {9, 10, 7, 8}, "len" -> 4, "cap" -> 4, "iFront" -> 3, "iBack" -> 3|>}



Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

list manipulation - Selecting multiple columns from a matrix?

Sample data: data = { {{2013, 1, 1}, 24.13, 167.67, 231.82}, {{2013, 1, 2}, 32.15, 170.92, 225.99}, {{2013, 1, 3}, 35.43, 172.68, 221.67}, {{2013, 1, 4}, 36.73, 173.05, 218.32}, {{2013, 1, 5}, 58.19, 165.96, 197.05}, {{2013, 1, 6}, 69.99, 163.50, 187.52}, {{2013, 1, 7}, 71.37, 154.21, 175.58}, {{2013, 1, 8}, 72.51, 149.66, 163.25}}; I want a DateListPlot with three graphs, so for a matrix formed by columns 1 and 2, one for columns 1 and 3, and 1 for columns 1 and 4. At the moment I'm using this code: data2 = Transpose[{data[[All, 1]], data[[All, 2]]}]; data3 = Transpose[{data[[All, 1]], data[[All, 3]]}]; data4 = Transpose[{data[[All, 1]], data[[All, 4]]}]; DateListPlot[{data2, data3, data4}, Joined -> True, Filling -> {3 -> {1}}] but I have a hunch that this can be done more efficiently. I don't like the Transpose s in particular. Any ideas? edit (for extra credit) What if I need to multiply the second column by 2, which in my solution is simp...